\
一、前言
工作中经常要对存贮在txt文件ADC采样的数据进行处理,虽然有着现成的工具软件可以对数据进行分析,然而不具有针对性,因此需要根据不同的数据处理要求,采用MFC编程实现,那么问题就在于如何通过编程实现对文本文件中数据的读取。
二、CStdioFile类
CStdioFile类的声明保存在afx.h头文件中。
CStdioFile类继承自CFile类,CStdioFile对象表示一个C运行函数fopen打开的的流式文件。流式文件是被缓冲的,而且可以以文本方式(默认)或者二进制方式打开。
CStdioFile类不支持CFile类中的Duplicate、LockRange、UnlockRange函数,如果你使用了,会得到C Not Supported Exception类的错误。
CStringFile类默认的是按照Text模式操作文件。
CFile 类默认的是按照二进制模式操作文件。
这里大致说明一下二进制模式和Text模式的区别。
二进制模式:对于一行的结尾我们必须输入'\r\n',才能表示回车换行的效果。
Text模式:'\r'回车的工作是自动完成的,我们只需要写入'\n'即可。在使用Text模式时从外部读入文件时,'\r\n'会被翻译成'\n',写入文件时,我们对于回车换行只需提供'\n',即可,'\r\n'会被写入到文件中。
成员变量
m_pStream成员变量:打开文件的指针。
构造函数
CStdioFile();
CStdioFile(FILE *pOpenStream);
CStdioFile(LPCTSTR lpFileName, UINT nOpenFlags);
throw(CFileException);
FILE *pOpenStream:指的是c运行函数fopen调用后返回的文件指针。
LPCTSTR lpFileName:指的是被打开的文件(绝对地址或相对地址)
UINT nOpenFlags:指的是CFile类中所描述的打开文件的方式。
读函数
virtual LPTSTR ReadString(LPTSTR lpsz, UINT nMax);
throw(CFileException);
如果使用该函数读取文本文件,当遇到'\r\n',停止读取,并去掉'\r',保留'\n',并在字符串尾部增加'\0',nMax的长度包含有'\0'字符,实际的分析如下:
如果nMax <= 字符数,读取(nMax-1)个字符+0x00;
如果nMax = 字符数 + 1,读取nMax个字符+0x00;
如果nMax > 字符数,读取nMax个字符+0x0A('\n') + 0x00;
如果文件有多行,则当文件没有读完时,返回NOT NULL,读到文件尾,返回NULL。
BOOL ReadString(CString& rString);
throw(CFileException);
读取一行文本到rString中,遇到回车换行符停止读取,回车和换行符均不读到rString中,尾部也不添加'0x00'。如果文件有多行,则当文件没有读完时,返回TRUE,读到文件尾,返回FALSE。
写函数
virtual void WriteString(LPTSTR
throw(CFileException);
将缓冲区中的数据写入到与CStdioFile对象相关联的文件中,不支持CString类型数据写入,结束的'\0'不被写入到文件中,lpsz缓冲区中的所有换行符被替换为回车换行符,即'\n'转换为'\r\n'。
三、CStdioFile类编程原理
假如要进行的文件操作只是简单的读写整行的字符串,那么最好使用CStdioFile。首先把文本文件的每行数据读到一个缓冲区,然后使用sscanf把它转化为字符格式。
例如在一个txt文件里每一行数据格式是这样的:
A1 A2 A3 A3 ......An
那么读取的主体代码是:
CStdioFile File;
CString FileData; // 定义一个CString,作为一个缓冲区
//定义n个临时字符串变量,大小依据实际情况,这里暂设为10
char TempStr1[10],TempStr2[10]......TempStrN[10];
File.ReadString(FileData); // 将一行数据读到缓冲区
//将该行数据的n个字符读到n个临时字符串变量
sscanf(FileData,"%s %s %s %s ......%s",TempStr1,TempStr2......TempStrN);
这种读法的一个好处是对文本格式要求不严,如下面的格式也可以
(前面可有未知个空格) A1 A2 (两个数据之间也可有未知个空格) A3 A3 ......An
四、CStdioFile类编程应用
以一个单文档程序为例。该程序的主要功能是读取文本文件的坐标数据,然后在客户区里用直线将这些坐标连起来,并显示。
1、 启动Visual C++6.0,生成一个单文档的工程,将该工程命名为ReadCoodinate。
2、 添加一个“读取文本数据”的菜单项。
3、 给视图类添加两个public变量:
CArray<CPoint,CPoint> m_PointArray; // 用于记录坐标点数据
int m_PointNum; // 用于记录坐标点个数,在视图类构造函数中初始化为0。
4、 给“读取文本数据”添加相应的单击消息响应函数。代码如下:
void CReadCoodinateView::OnReaddata()
{
// TODO: Add your command handler code here
CFileDialog dlg(TRUE); // 定义一个文件对话框变量
if(dlg.DoModal()==IDOK)
{
CString m_FilePath = dlg.GetPathName(); //取得文件路径及文件名
CStdioFile File;
File.Open(m_FilePath,CFile::modeRead); //以读模式打开文本文件
CString FileData; //定义一个CString变量作为缓冲区
//定义两个临时字符串,并初始化为'\0'
char TempStr1[10];
char TempStr2[10];
memset(TempStr1,'\0',10);
memset(TempStr2,'\0',10);
File.ReadString(FileData);//读取第一行数据,第一行数据为坐标点数据
sscanf(FileData,"%s",TempStr1);
m_PointNum = atoi(TempStr1); // 获取坐标点个数
//逐行读取坐标数据
for (int i = 0;i<m_PointNum;i++)
{
File.ReadString(FileData);
sscanf(FileData,"%s %s",TempStr1,TempStr2);
m_PointArray.Add(CPoint(atoi(TempStr1),atoi(TempStr2)));//将其存入坐标点数组
}
CDC *pDC = GetDC(); //获取设备环境;
//根据坐标点绘出直线
for (i = 0;i<m_PointNum-1;i++)
{
pDC->MoveTo(m_PointArray[i].x, m_PointArray[i].y);
pDC->LineTo(m_PointArray[i+1].x, m_PointArray[i+1].y);
}
ReleaseDC(pDC); //使用完后,释放设备环境
}
}
其中示例数据文件的格式是这样的:(第一行为坐标个数,余下的是坐标点数据)
5
10 20
30 40
45 85
100 120
200 300
这个程序的一个优点是对文本数据格式并不严格,只要x坐标和y坐标中间有一个空格就可以了。若要将文本数据传化为其它形式的数据(如将字符型数据转化为浮点型可以利用函数atof()),其中原理是一样的。