本文档将介绍文本格式文件的读写,常见的文本资料主要有:
层位(.txt
) 切片(.txt
) 测井(.las
、.txt
) 断层(.txt
)等。这些资料实质上均为表格(Table),由表头和数据组成。
本次练习将对切片(Slice)文件进行读写操作,常见的切片数据样例如下:
新建头文件SliceDataIO.h与C++文件SliceDataIO.cpp,以及主函数main.cpp。
1 编写头文件SliceData.h 1.1 程序描述、调用、声明、定义/**********************************************************************
* Copyright(C) 2018,Company All Rights Reserved
*
* @file : SliceData.cpp
*
* @brief : 实现文本数据的读、写操作
*
* @version : 1.0
*
* @author : Fan XinRan
*
* @date : 2022/2/8 星期二
*
* Others :
**********************************************************************/
#pragma once
#include<stdio.h> //C Language header file
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<iostream> // C++ header file
#include<vector>
#include<algorithm>
#include"alloc.h" // 用于创建多维数组
#define PI 3.141592654
#define EPS 0.000001
using namespace std;
1.2 定义类
类描述了一种数据类型的全部属性(包括可使用它执行的操作),对象是根据这些描述创建的实体。
class Classname { // class + 类名
public: // 公有成员 在程序中类的外部是可访问的,可以不使用任何成员函数来设置和获取公有变量的值
float x;
private: // 私有成员 私有成员变量或函数在类的外部是不可访问的,甚至不可查看的
float _y;
protected: // 受保护成员 与私有成员相似,不同之处为,受保护成员在子类中是可访问的
float z;
}
(1) 私有成员
通常情况下都会设置类成员状态为私有(private),以保证良好的封装性。私有成员中的变量和函数一般以(_
)开头。
class SliceDataIO{ // 定义一个名为SliceDataIO的类
private: // 私有成员
int *_sliceIndex; // 1 切片号
int *_lineIndex; // 2 线号
int *_cdpIndex; // 3 道号
float *_Xcordinate; // 4 X坐标
float *_Ycordinate; // 5 Y坐标
float *_time; // 6 时间
float *_value; // 7 属性值
int _nsample; // 采样点数
public: // 公有成员
...
};
(2) 公有成员
由于隐藏数据是OOP(面向对象编程)的主要目标之一,因此数据项通常放在私有部分。公有成员函数是程序和对象的私有成员之间的桥梁,通过设计公有成员函数以获取隐藏的数据。
class SliceDataIO{
private:
...
public: // 公有成员
SliceDataIO(); // 默认构造函数 没有参数,将创建SliceDataIO类对象,但不初始化其成员
SliceDataIO(int nsample); // 创建一个具有nsample个采样点的类对象
~SliceDataIO(); // 析构函数
// 获取数据get()
int *getSliceIndex(); // 1 声明成员函数:获取切片号
int *getLineIndex(); // 2 ...
int *getCDPIndex(); // 3 ...
float *getX(); // 4 ...
float *getY(); // 5 ...
float *getTime(); // 6 ...
float *getValue(); // 7 获取属性值
int getSampleNum(); // 声明成员函数:获取采样点数
// 设定数据set()
bool setData(vector<int> IndexVector,vector<float> XVector,vector<float> YVector,vector<int> LineVector,vector<int> CDPVector,vector<float> ValueVector,vector<float> TimeVector);
}
-
构造函数:
- 是类的一种特殊的成员函数,它会在每次创建类的新对象时执行;
- 构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。
- 默认构造函数没有任何参数,带参数的构造函数则会在创建对象时为其赋初始值。
-
析构函数:
- 是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行;
- 析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(
~
)作为前缀,它不会返回任何值,也不能带有任何参数; - 析构函数完成清理工作,在跳出程序(比如关闭文件、释放内存等)前释放资源。
-
vector(向量):是一个能够存放任意类型的动态数组,可在末尾附加新数据,或在中间插入新数据。
声明读、写函数,其中读函数由SliceDataIO
类实现。
SliceDataIO *readSliceData(const char *filenameInput); // read slice data from Inputfile
bool writeSliceData(const char *filenameOutput, SliceDataIO *slicedata) // write slice data to Outputfile
完整代码
#pragma once
#include<stdio.h> //C Language header file
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<iostream> //C++ header file
#include<vector>
#include<algorithm>
#include"alloc.h"
#define PI 3.141592654 // 定义全局常量
#define EPS 0.000001
using namespace std; // 声明命名空间
class SliceDataIO{ // 定义一个名为SliceDataIO的类,用于切片数据的读写
public: // 公有成员
SliceDataIO();
SliceDataIO(int nsample);
~SliceDataIO();
// 获取隐藏数据
int *getSliceIndex();
int *getLineIndex();
int *getCDPIndex();
float *getX();
float *getY();
float *getTime();
float *getValue();
int getSampleNum();
// 设定数据
bool setData(vector<int> IndexVector,vector<float> XVector,vector<float> YVector,vector<int> LineVector,vector<int> CDPVector,vector<float> ValueVector,vector<float> TimeVector);
private: // 私有成员
// 声明私有变量,包括切片号、线号、道号等
int *_sliceIndex;
int *_lineIndex;
int *_cdpIndex;
float *_Xcordinate;
float *_Ycordinate;
float *_time;
float *_value;
int _nsample;
};
// 声明读、写函数
SliceDataIO *readSliceData(const char *filenameInput);
bool writeSliceData(const char *filenameOutput, SliceDataIO *slicedata);
2 编写C++文件SliceData.cpp
2.1 定义类成员函数
类成员函数是类的一个成员,它可以操作类的任意对象,也可以访问对象中的所有成员。成员函数可以定义在类定义内部,或者单独使用作用域解析运算符(::
)来定义,此时(::
)前须加类名以标识函数所属的类。
// 默认构造函数
SliceDataIO::SliceDataIO(){ // 默认构造函数,将成员初始化为NULL或0
this->_sliceIndex = NULL; // 1 初始化切片号
this->_lineIndex = NULL; // 2 初始化线号
this->_cdpIndex = NULL; // 3 初始化道号
this->_Xcordinate = NULL; // 4 初始化X坐标
this->_Ycordinate = NULL; // 5 初始化Y坐标
this->_time = NULL; // 6 初始化时间值
this->_value = NULL; // 7 初始化属性值
this->_nsample = 0; // 初始化采样点数
}
// 带参数的构造函数
SliceDataIO::SliceDataIO(int nsample){ // 创建一个具有nsample个采样点的类对象,并为其成员分配内存
this->_nsample = nsample; // 输入采样点数nsample,根据采样点数分配内存
this->_sliceIndex = (int*)calloc(this->_nsample, sizeof(int));
this->_lineIndex = (int*)calloc(this->_nsample,sizeof(int));
this->_cdpIndex= (int*)calloc(this->_nsample, sizeof(int));
this->_Xcordinate= (float*)calloc(this->_nsample, sizeof(float));
this->_Ycordinate = (float*)calloc(this->_nsample, sizeof(float));
this->_time = (float*)calloc(this->_nsample,sizeof(float));
this->_value= (float*)calloc(this->_nsample, sizeof(float));
}
(2)析构函数
// 析构函数
SliceDataIO::~SliceDataIO(){ // 析构函数完成清理工作
if(this->_cdpIndex!=NULL){
free(this->_cdpIndex); // 释放内存
this->_cdpIndex = NULL; // 成员置空
}
if(this->_lineIndex!=NULL){
free(this->_lineIndex);
this->_lineIndex = NULL;
}
if(this->_sliceIndex!=NULL){
free(this->_sliceIndex);
this->_sliceIndex = NULL;
}
if(this->_Xcordinate!=NULL){
free(this->_Xcordinate);
this->_Xcordinate = NULL;
}
if(this->_Ycordinate!=NULL){
free(this->_Ycordinate);
this->_Ycordinate = NULL;
}
if(this->_time!=NULL){
free(this->_time);
this->_time = NULL;
}
if(this->_value!=NULL){
free(this->_value);
this->_value = NULL;
}
}
this
:- 它是一种特殊的指针,每一个对象都能通过 this 指针来访问自己的地址;
- this 指针是所有成员函数的隐含参数,因此,在成员函数内部,它可以用来指向调用对象;
- 只有成员函数才有 this 指针。
->
:称为箭头运算符。在调用类成员时,使用点运算符(.
)或箭头运算符(->
),点运算符应用于实际的对象;箭头运算符与一个指向对象的指针一起使用。
首先是get方法,用于获取数据;
int* SliceDataIO::getSliceIndex(){ //定义SliceDataIO的类成员函数getSliceIndex以获取切片号
return this->_sliceIndex;
}
int*SliceDataIO::getLineIndex(){
return this->_lineIndex;
}
int* SliceDataIO::getCDPIndex(){
return this->_cdpIndex;
}
float*SliceDataIO::getX(){
return this->_Xcordinate;
}
float*SliceDataIO::getY(){
return this->_Ycordinate;
}
float*SliceDataIO::getTime() {
return this->_time;
}
float*SliceDataIO::getValue() {
return this->_value;
}
int SliceDataIO::getSampleNum(){
return this->_nsample;
}
(4)set()方法函数
然后是set方法,用于指定数据为当前类对象的值。
bool SliceDataIO::setData(vector<int> IndexVector, vector<float> XVector, vector<float> YVector, vector<int> LineVector, vector<int> CDPVector, vector<float> ValueVector, vector<float> TimeVector){
// 函数接收若干个向量,并将向量值赋给当前的类对象
for(int isample=0; isample < this->_nsample;isample++){
this->_sliceIndex[isample] = IndexVector[isample];
this->_lineIndex[isample] = LineVector[isample];
this->_cdpIndex[isample] = CDPVector[isample];
this->_Xcordinate[isample] = XVector[isample];
this->_Ycordinate[isample] = YVector[isample];
this->_value[isample] = ValueVector[isample];
this->_time[isample] = TimeVector[isample];
}
return true;
}
2.2 定义读取函数
SliceDataIO *readSliceData(const char *filenameInput){ // 定义读取Slice数据的函数
int Index_temp = 0; // 创建待处理的切片参数,并按类型分别初始化。用于临时存放单一采样点数据
int CDP_temp = 0;
int LINE_temp = 0;
float X_temp = 0.0f;
float Y_temp = 0.0f;
float Time_temp = 0.0f;
float Value_temp = 0.0f;
bool flag = true; // 判断程序是否正常运行
int k = 0; // 当前循环次数
int n1 = 1; // 判定输入是否正常
int nPoint = 0; // 向量(vector)中存放的样本点数
vector<int> IndexVector; // 分别创建vector用于存放切片号、道号、线号等信息
vector<int> LineVector;
vector<int> CDPVector;
vector<float> XVector;
vector<float> YVector;
vector<float> TimeVector;
vector<float> ValueVector;
FILE *fp_input = NULL; // 创建输入指针
fp_input = fopen(filenameInput, "rt"); // 以"rt"方式,即读取文本的方式打开文件
if(fp_input==NULL){ // 非空判定
printf("Can not open %s file!!!\n", filenameInput);
return NULL;
}
fscanf(fp_input,"%*[^\n]%*c"); // " "内为固定正则表达,用于忽略*********************************
while(flag == true){ // 当程序正常运行时
n1 = fscanf(fp_input,"%d %d %d %f %f %f %f", &Index_temp,&LINE_temp,&CDP_temp,&X_temp,&Y_temp,&Time_temp,&Value_temp); // 从输入流中分别获取需要的数据
if(n1<0){
flag = false; // 如果读取异常,循环中止
}else{
IndexVector.push_back(Index_temp);
LineVector.push_back(LINE_temp);
CDPVector.push_back(CDP_temp);
XVector.push_back(X_temp);
YVector.push_back(Y_temp);
TimeVector.push_back(Time_temp);
ValueVector.push_back(Value_temp); // 读取正常,追加新数据到向量中
k = k + 1;
}//end if(n1<0)
if(k%1000==0){ // 每1000轮显示一次进度
printf(" Read numTrace=%d\n",k);
}//end if(k%1000==0)
}//end while(flag == true)
nPoint = ValueVector.size(); // 获取vector中存放的数据条数
SliceDataIO *data = new SliceDataIO(nPoint); // 分配内存
// 为新建的空白对象data设定值,类似粘贴
data->setData(IndexVector, XVector, YVector, LineVector, CDPVector, ValueVector, TimeVector);
vector<int>().swap(IndexVector); // 每个向量与空向量交换,即将存有数据的向量置空
vector<int>().swap(LineVector);
vector<int>().swap(CDPVector);
vector<float>().swap(XVector);
vector<float>().swap(YVector);
vector<float>().swap(TimeVector);
vector<float>().swap(ValueVector);
fclose(fp_input); // 关闭输入文件指针
return data; // 返回数据data
}
-
fscanf()
从流 stream 读取格式化输入;int fscanf(FILE *stream, const char *format, ...)
-
stream-- 指向FILE对象的指针;
-
format-- 这是 C 字符串,包含了以下各项中的一个或多个:空格字符、非空格字符和 format 说明符;
format说明符形式为
[=%[*][width][modifiers]type=]
。
-
-
new
:为任意的数据类型动态分配内存;用法有以下两种:Type *p = new Type;
其中,Type
是任意类型名,p
是类型为Type
的指针;Type *p =new Type[N];
用来动态分配一个任意大小的数组,N
代表元素个数;
与
malloc()
函数相比,new
的主要的优点是,其不只分配了内存,还创建了对象。
bool writeSliceData(const char *filenameOutput, SliceDataIO *slicedata){
int nsample = slicedata->getSampleNum(); // 从类对象slicedata中get各类数据
int *Index = slicedata->getSliceIndex();
int *LineIndex = slicedata->getLineIndex();
int *CdpIndex = slicedata->getCDPIndex();
float *X = slicedata->getX();
float *Y = slicedata->getY();
float *Time = slicedata->getTime();
float *Value = slicedata->getValue();
FILE *fp_output = fopen(filenameOutput,"wt"); //以写入文本的方式打开Outputfile
fprintf(fp_output,"Index Line CDP X Y Time(ms) Value\n"); // 先写入表头
for (int isample=0; isample<nsample; isample++){ //遍历类对象中每一个采样点样本
fprintf(fp_output,"%d %d %d %f %f %f %f\n", Index[isample], LineIndex[isample], CdpIndex[isample],X[isample],Y[isample],Time[isample],Value[isample]); //写入到输出流中
if (isample % 1000 == 0) { // 同样,每迭代1000次打印一次进度
printf(" Write numTrace=%d\n", isample);
}//end if(k%1000==0)
}
fclose(fp_output); // 关闭输出文件指针
return true;
}
fprintf
:发送格式化输出到流 stream 中,用法与fscanf
相似;
int fprintf(FILE *stream, const char *format, ...)
-
stream-- 指向FILE对象的指针;
-
format-- 包含了要被写入到流
stream
中的文本。由以下各项中的一个或多个组成:空格字符、非空格字符和format说明符;format说明符形式为
[=%[*][width][modifiers]type=]
。
/*****************************************************************************
Function: SliceDataIO
Description: read and write slice data
Input:
const char *filenameInput [in] input filename (.txt)
Output:
const char *filenameOutput[out] output filename (.txt)
Return:
bool true program success
bool false program failed
Author: Fan XinRan
Date : 2022/2/8
Others:
*****************************************************************************/
#include "SliceDataIO.h"
//构造函数
SliceDataIO::SliceDataIO(){
this->_cdpIndex = NULL;
this->_lineIndex = NULL;
this->_sliceIndex = NULL;
this->_Xcordinate = NULL;
this->_Ycordinate = NULL;
this->_time = NULL;
this->_value = NULL;
this->_nsample = 0;
}
//带参数nsample的构造函数
SliceDataIO::SliceDataIO(int nsample){
this->_nsample = nsample;
this->_sliceIndex = (int*)calloc(this->_nsample, sizeof(int));
this->_lineIndex = (int*)calloc(this->_nsample,sizeof(int));
this->_cdpIndex= (int*)calloc(this->_nsample, sizeof(int));
this->_Xcordinate= (float*)calloc(this->_nsample, sizeof(float));
this->_Ycordinate = (float*)calloc(this->_nsample, sizeof(float));
this->_time = (float*)calloc(this->_nsample,sizeof(float));
this->_value= (float*)calloc(this->_nsample, sizeof(float));
}
//析构函数
SliceDataIO::~SliceDataIO(){
if(this->_cdpIndex!=NULL){
free(this->_cdpIndex);
this->_cdpIndex = NULL;
}
if(this->_lineIndex!=NULL){
free(this->_lineIndex);
this->_lineIndex = NULL;
}
if(this->_sliceIndex!=NULL){
free(this->_sliceIndex);
this->_sliceIndex = NULL;
}
if(this->_Xcordinate!=NULL){
free(this->_Xcordinate);
this->_Xcordinate = NULL;
}
if(this->_Ycordinate!=NULL){
free(this->_Ycordinate);
this->_Ycordinate = NULL;
}
if(this->_time!=NULL){
free(this->_time);
this->_time = NULL;
}
if(this->_value!=NULL){
free(this->_value);
this->_value = NULL;
}
}
//======================= get方法========================
int* SliceDataIO::getSliceIndex(){
return this->_sliceIndex;
}
int*SliceDataIO::getLineIndex(){
return this->_lineIndex;
}
int* SliceDataIO::getCDPIndex(){
return this->_cdpIndex;
}
float*SliceDataIO::getX(){
return this->_Xcordinate;
}
float*SliceDataIO::getY(){
return this->_Ycordinate;
}
float*SliceDataIO::getTime() {
return this->_time;
}
float*SliceDataIO::getValue() {
return this->_value;
}
int SliceDataIO::getSampleNum(){
return this->_nsample;
}
// set方法
bool SliceDataIO::setData(vector<int> IndexVector, vector<float> XVector, vector<float> YVector, vector<int> LineVector, vector<int> CDPVector, vector<float> ValueVector, vector<float> TimeVector){
for(int isample=0; isample < this->_nsample;isample++){
this->_sliceIndex[isample] = IndexVector[isample];
this->_lineIndex[isample] = LineVector[isample];
this->_cdpIndex[isample] = CDPVector[isample];
this->_Xcordinate[isample] = XVector[isample];
this->_Ycordinate[isample] = YVector[isample];
this->_value[isample] = ValueVector[isample];
this->_time[isample] = TimeVector[isample];
}
return true;
}
// 读取切片数据函数
SliceDataIO *readSliceData(const char *filenameInput){
int Index_temp = 0;
float X_temp = 0.0f;
float Y_temp = 0.0f;
int CDP_temp = 0;
int LINE_temp = 0;
float Time_temp = 0.0f;
float Value_temp = 0.0f;
bool flag = true;
int k = 0;
int n1 = 1;
int nPoint = 0;
vector<int> IndexVector;
vector<float> XVector;
vector<float> YVector;
vector<int> LineVector;
vector<int> CDPVector;
vector<float> ValueVector;
vector<float> TimeVector;
FILE *fp_input = NULL;
fp_input = fopen(filenameInput, "rt");
if(fp_input==NULL){
printf("Can not open %s file!!!\n", filenameInput);
return NULL;
}
fscanf(fp_input,"%*[^\n]%*c");
while(flag == true){
n1 = fscanf(fp_input,"%d %d %d %f %f %f %f",&Index_temp,&LINE_temp,&CDP_temp,&X_temp,&Y_temp,&Time_temp,&Value_temp);
if(n1<0){
flag = false;
}else{
IndexVector.push_back(Index_temp);
LineVector.push_back(LINE_temp);
CDPVector.push_back(CDP_temp);
XVector.push_back(X_temp);
YVector.push_back(Y_temp);
TimeVector.push_back(Time_temp);
ValueVector.push_back(Value_temp);
k = k + 1;
}//end if(n1<0)
if(k%1000==0){
printf(" Read numTrace=%d\n",k);
}//end if(k%1000==0)
}//end while(flag == true)
nPoint = ValueVector.size();
SliceDataIO *data = new SliceDataIO(nPoint);
data->setData(IndexVector, XVector, YVector, LineVector, CDPVector, ValueVector, TimeVector);
vector<int>().swap(IndexVector);
vector<int>().swap(LineVector);
vector<int>().swap(CDPVector);
vector<float>().swap(XVector);
vector<float>().swap(YVector);
vector<float>().swap(TimeVector);
vector<float>().swap(ValueVector);
fclose(fp_input);
return data;
}
// 写入切片数据
bool writeSliceData(const char *filenameOutput, SliceDataIO *slicedata){
int nsample = slicedata->getSampleNum();
int *Index = slicedata->getSliceIndex();
int *LineIndex = slicedata->getLineIndex();
int *CdpIndex = slicedata->getCDPIndex();
float *X = slicedata->getX();
float *Y = slicedata->getY();
float *Time = slicedata->getTime();
float *Value = slicedata->getValue();
FILE *fp_output = fopen(filenameOutput,"wt");
fprintf(fp_output,"Index Line CDP X Y Time(ms) Value\n");
for (int isample=0; isample<nsample; isample++){
fprintf(fp_output,"%d %d %d %f %f %f %f\n", Index[isample], LineIndex[isample], CdpIndex[isample],X[isample],Y[isample],Time[isample],Value[isample]);
if (isample % 1000 == 0) {
printf(" Write numTrace=%d\n", isample);
}//end if(k%1000==0)
}
fclose(fp_output);
return true;
}
3 编写主函数main.cpp
#include"SliceCurvatrue.h"
#include"SliceDataIO.h"
int main(int argc, char *agrv) {
SliceDataIO *slice = readSliceData("W11.slice"); // 读入切片数据
writeSliceData("W11copy.slice", slice); // 写入切片数据
delete slice; // 释放分配给类对象slice的动态内存空间
return 1;
}
运行主函数之后,程序将读入W11.slice
,并将切片数据写至W11copy.slice
中。
delete
:用以释放动态分配的内存空间,要求被释放的指针必须是指向动态分配的内存空间。
- 使用时需调用头文件
#include<vector>
中; - 创建vector对象,形如
vector<int> vec
,vector的元素可以是int,double,string或结构体; - 使用
[ ]
访问元素,如vec[0]
; - 尾部添加元素:
vec.push_back(a)
; - 插入元素:
vec.insert(vec.begin()+i,a)
在第i+1
个元素前插入a
; - 删除元素;
vec.erase(vec.begin()+2)
删除第3个元素; - 两vector交换数据:
a.swap(b)
交换向量a和b的值; - 向量大小:
vec.size()
; - 清空向量:
vec.clear()
;
需要头文件#include<algorithm>
- 使用
reverse
将元素翻转:reverse(vec.begin(),vec.end())
- 使用sort排序:
sort(vec.begin(),vec.end())