MFC中的文件读写方法总结

来源:互联网 发布:淘宝的车秒贷怎么样 编辑:程序博客网 时间:2024/05/19 12:38
1.C中
文件写入:
FILE *pFile=fopen("1.txt","w");fwrite("http://www.sunxin.org",1,strlen("http://www.sunxin.org"),pFile);//fseek(pFile,0,SEEK_SET);    //seek_set挪移至文件开头,一直从开头加入   SEEK_END挪移至末尾,从末尾添加//fwrite("ftp:",1,strlen("ftp:"),pFile);//fwrite("http://www.sunxin.org",1,strlen("http://www.sunxin.org"),pFile);fclose(pFile);*///fflush(pFile);        //效果:将文件产生的修改提交到磁盘中而不需要关闭文件

文件读取:
void CFileView::OnFileRead(){    FILE *pFile = fopen("1.txt", "r");    char *pBuf;    fseek(pFile, 0, SEEK_END);    int len = ftell(pFile);    pBuf = new char[len+1];    rewind(pFile)    //指针复位    fread(pBuf, 1, len, pFile);    pBuf[len] = 0;    fclose(pFile);    MessageBox(pBuf);}

二进制文件和文本文件:
当按照文本文件方式向文件中写入数据时,一旦遇到“换行”字符(ASCII码为10),则会转换为“回车-换行”(ASCII: 13,10)。在读取文件时,一旦遇到“回车-换行”的组合,则会转换为换行字符。(导致的结果:写入多一位,读取少一位。)当按照二进制方式向文件中写入数据时,则会将数据在内存中的存储形式原样输出
多字节对象在计算机的存储方式有两种排列规则:
little endian:低字节在先,高字节在后。
big endian: 高字节在先,低字节在后。
2.C++中
ofstream ofs("4.txt");ofs.write("http://www.sunxin.org",strlen("http://www.sunxin.org"));ofs.close();
要包括头文件 "fstream.h"
相应的读取流函数ifstream.
void CFileView::OnFileRead(){    ifstream ifs("4.txt");    char ch[100];    memset(ch,0,100);    ifs.read(ch,100);    ifs.close();    MessageBox(ch);}

C++ 通过以下几个类支持文件的输入输出:

  • ofstream: 写操作(输出)的文件类 (由ostream引申而来)
  • ifstream: 读操作(输入)的文件类(由istream引申而来)
  • fstream: 可同时读写操作的文件类 (由iostream引申而来)
打开文件(Open a file)

对这些类的一个对象所做的第一个操作通常就是将它和一个真正的文件联系起来,也就是说打开一个文件。被打开的文件在程序中由一个流对象(stream object)来表示 (这些类的一个实例) ,而对这个流对象所做的任何输入输出操作实际就是对该文件所做的操作。

要通过一个流对象打开一个文件,我们使用它的成员函数open():

void open (const char * filename, openmode mode);

这里filename 是一个字符串,代表要打开的文件名,mode 是以下标志符的一个组合:

  • ios::in 为输入(读)而打开文件 
  • ios::out 为输出(写)而打开文件 
  • ios::ate 初始位置:文件尾 
  • ios::app 所有输出附加在文件末尾 
  • ios::trunc 如果文件已存在则先删除该文件 
  • ios::binary 二进制方式

这些标识符可以被组合使用,中间以”或”操作符(|)间隔。例如,如果我们想要以二进制方式打开文件"example.bin" 来写入一些数据,我们可以通过以下方式调用成员函数open()来实现:

ofstream file;file.open ("example.bin", ios::out | ios::app | ios::binary);

ofstream, ifstream 和 fstream所有这些类的成员函数open 都包含了一个默认打开文件的方式,这三个类的默认方式各不相同:

类 参数的默认方式 
  • ofstream ios::out | ios::trunc 
  • ifstream ios::in 
  • fstream ios::in | ios::out

只有当函数被调用时没有声明方式参数的情况下,默认值才会被采用。如果函数被调用时声明了任何参数,默认值将被完全改写,而不会与调用参数组合

由于对类ofstream, ifstream 和 fstream 的对象所进行的第一个操作通常都是打开文件,这些类都有一个构造函数可以直接调用open 函数,并拥有同样的参数。这样,我们就可以通过以下方式进行与上面同样的定义对象和打开文件的操作:

ofstream file ("example.bin", ios::out | ios::app | ios::binary);

两种打开文件的方式都是正确的。

你可以通过调用成员函数is_open()来检查一个文件是否已经被顺利的打开了:

bool is_open();

它返回一个布尔(bool)值,为真(true)代表文件已经被顺利打开,假( false )则相反。


关闭文件(Closing a file)

当文件读写操作完成之后,我们必须将文件关闭以使文件重新变为可访问的。关闭文件需要调用成员函数close(),它负责将缓存中的数据排放出来并关闭文件。它的格式很简单:

void close ();

这个函数一旦被调用,原先的流对象(stream object)就可以被用来打开其它的文件了,这个文件也就可以重新被其它的进程(process)所有访问了。

为防止流对象被销毁时还联系着打开的文件,析构函数(destructor)将会自动调用关闭函数close


文本文件(Text mode files)

类ofstream, ifstream 和fstream 是分别从ostream, istream 和iostream 中引申而来的。这就是为什么 fstream 的对象可以使用其父类的成员来访问数据。

一般来说,我们将使用这些类与同控制台(console)交互同样的成员函数(cin 和 cout)来进行输入输出。如下面的例题所示,我们使用重载的插入操作符<<

    // writing on a text file#include <fiostream.h> int main () {  ofstream examplefile ("example.txt");  if (examplefile.is_open())   {    examplefile << "This is a line.\n";    examplefile << "This is another line.\n";    examplefile.close();  }  return 0;}file example.txt This is a line.This is another line.


从文件中读入数据也可以用与 cin的使用同样的方法:
    // reading a text file#include <iostream.h>#include <fstream.h>#include <stdlib.h> int main () {  char buffer[256];  ifstream examplefile ("example.txt");  if (! examplefile.is_open())  {     cout << "Error opening file"; exit (1);   }  while (! examplefile.eof() )   {    examplefile.getline (buffer,100);    cout << buffer << endl;  }  return 0;}This is a line.This is another line.


上面的例子读入一个文本文件的内容,然后将它打印到屏幕上。注意我们使用了一个新的成员函数叫做eof ,它是ifstream 从类 ios 中继承过来的,当到达文件末尾时返回true 。


状态标志符的验证(Verification of state flags)

除了eof()以外,还有一些验证流的状态的成员函数(所有都返回bool型返回值):

  • bad()  如果在读写过程中出错,返回 true 。例如:当我们要对一个不是打开为写状态的文件进行写入时,或者我们要写入的设备没有剩余空间的时候。
  • fail()  除了与bad() 同样的情况下会返回 true 以外,加上格式错误时也返回true ,例如当想要读入一个整数,而获得了一个字母的时候。
  • eof()  如果读文件到达文件末尾,返回true。
  • good()  这是最通用的:如果调用以上任何一个函数返回true 的话,此函数返回 false 。


要想重置以上成员函数所检查的状态标志,你可以使用成员函数clear(),没有参数。


获得和设置流指针(get and put stream pointers)

所有输入/输出流对象(i/o streams objects)都有至少一个流指针:

  • ifstream, 类似istream, 有一个被称为get pointer的指针,指向下一个将被读取的元素。
  • ofstream, 类似 ostream, 有一个指针 put pointer ,指向写入下一个元素的位置。
  • fstream, 类似 iostream, 同时继承了get 和 put

我们可以通过使用以下成员函数来读出或配置这些指向流中读写位置的流指针:

tellg() 和 tellp()
这两个成员函数不用传入参数,返回pos_type 类型的值(根据ANSI-C++ 标准) ,就是一个整数,代表当前get 流指针的位置 (用tellg) 或 put 流指针的位置(用tellp).
seekg() 和seekp()
这对函数分别用来改变流指针get 和put的位置。两个函数都被重载为两种不同的原型:
seekg ( pos_type position );seekp ( pos_type position );
使用这个原型,流指针被改变为指向从文件开始计算的一个绝对位置。要求传入的参数类型与函数 tellg 和tellp 的返回值类型相同。
seekg ( off_type offset, seekdir direction );seekp ( off_type offset, seekdir direction );

使用这个原型可以指定由参数direction决定的一个具体的指针开始计算的一个位移(offset)。它可以是:

  • ios::beg 从流开始位置计算的位移 
  • ios::cur 从流指针当前位置开始计算的位移 
  • ios::end 从流末尾处开始计算的位移

流指针 get 和 put 的值对文本文件(text file)和二进制文件(binary file)的计算方法都是不同的,因为文本模式的文件中某些特殊字符可能被修改。由于这个原因,建议对以文本文件模式打开的文件总是使用seekg 和 seekp的第一种原型,而且不要对tellg 或 tellp 的返回值进行修改。对二进制文件,你可以任意使用这些函数,应该不会有任何意外的行为产生。

以下例子使用这些函数来获得一个二进制文件的大小:

// obtaining file size#include <iostream.h>#include <fstream.h>const char * filename = "example.txt";int main (){  long l,m;  ifstream file (filename, ios::in|ios::binary);  l = file.tellg();  file.seekg (0, ios::end);  m = file.tellg();  file.close();  cout << "size of " << filename;  cout << " is " << (m-l) << " bytes.\n";  return 0;}size of example.txt is 40 bytes. 

二进制文件(Binary files)

在二进制文件中,使用<< 和>>,以及函数(如getline)来操作符输入和输出数据,没有什么实际意义,虽然它们是符合语法的。

文件流包括两个为顺序读写数据特殊设计的成员函数write 和 read。第一个函数 (write) 是ostream 的一个成员函数,都是被ofstream所继承。而read 是istream 的一个成员函数,被ifstream 所继承。类 fstream 的对象同时拥有这两个函数。它们的原型是:

write ( char * buffer, streamsize size );read ( char * buffer, streamsize size );

这里 buffer 是一块内存的地址,用来存储或读出数据。参数size 是一个整数值,表示要从缓存(buffer)中读出或写入的字符数。

// reading binary file#include <iostream>#include <fstream.h> const char * filename = "example.txt"; int main () {  char * buffer;  long size;  ifstream file (filename, ios::in|ios::binary|ios::ate);  size = file.tellg();  file.seekg (0, ios::beg);  buffer = new char [size];  file.read (buffer, size);  file.close();  cout << "the complete file is in a buffer";  delete[] buffer;  return 0;}The complete file is in a buffer 

缓存和同步(Buffers and Synchronization)

当我们对文件流进行操作的时候,它们与一个streambuf 类型的缓存(buffer)联系在一起。这个缓存(buffer)实际是一块内存空间,作为流(stream)和物理文件的媒介。例如,对于一个输出流, 每次成员函数put (写一个单个字符)被调用,这个字符不是直接被写入该输出流所对应的物理文件中的,而是首先被插入到该流的缓存(buffer)中。

当缓存被排放出来(flush)时,它里面的所有数据或者被写入物理媒质中(如果是一个输出流的话),或者简单的被抹掉(如果是一个输入流的话)。这个过程称为同步(synchronization),它会在以下任一情况下发生:

  • 当文件被关闭时在文件被关闭之前,所有还没有被完全写出或读取的缓存都将被同步。
  • 当缓存buffer 满时:缓存Buffers 有一定的空间限制。当缓存满时,它会被自动同步
  • 控制符明确指明:当遇到流中某些特定的控制符时,同步会发生。这些控制符包括:flush endl
  • 明确调用函数sync()调用成员函数sync() (无参数)可以引发立即同步。这个函数返回一个int 值,等于-1 表示流没有联系的缓存或操作失败。

在C++中,有一个stream这个类,所有的I/O都以这个“流”类为基础的,包括我们要认识的文件I/O,stream这个类有两个重要的运算符:
1、
插入器(<<) 
向流输出数据。比如说系统有一个默认的标准输出流(cout),一般情况下就是指的显示器,所以,cout<<"Write Stdout"<<'n';就表示把字符串"Write Stdout"和换行字符('n')输出到标准输出流。
2、析取器(>>) 
从流中输入数据。比如说系统有一个默认的标准输入流(cin),一般情况下就是指的键盘,所以,cin>>x;就表示从标准输入流中读取一个指定类型(即变量x的类型)的数据。
在C++中,对文件的操作是通过stream的子类fstream(file stream)来实现的,所以,要用这种方式操作文件,就必须加入头文件fstream.h。下面就把此类的文件操作过程一一道来。
一、打开文件 
在fstream类中,有一个成员函数open(),就是用来打开文件的,其原型是:

void open(const char* filename,int mode,int access);

参数:

  • filename: 要打开的文件名 
  • mode: 要打开文件的方式 
  • access: 打开文件的属性 
打开文件的方式在类ios(是所有流式I/O类的基类)中定义,常用的值如下:

  • ios::app: 以追加的方式打开文件 
  • ios::ate: 文件打开后定位到文件尾,ios:app就包含有此属性 
  • ios::binary: 以二进制方式打开文件,缺省的方式是文本方式。两种方式的区别见前文 
  • ios::in: 文件以输入方式打开 
  • ios::out: 文件以输出方式打开 
  • ios::nocreate: 不建立文件,所以文件不存在时打开失败 
  • ios::noreplace:不覆盖文件,所以打开文件时如果文件存在失败 
  • ios::trunc: 如果文件存在,把文件长度设为0 
可以用“或”把以上属性连接起来,如

ios::out|ios::binary

打开文件的属性取值是:

0:普通文件,打开访问 
1:只读文件 
2:隐含文件 
4:系统文件 
可以用“或”或者“+”把以上属性连接起来 ,如3或1|2就是以只读和隐含属性打开文件。


三、读写文件 
读写文件分为文本文件和二进制文件的读取,对于文本文件的读取比较简单,用
插入器析取器就可以了;而对于二进制的读取就要复杂些,下要就详细的介绍这两种方式

1、文本文件的读写 
文本文件的读写很简单:
用插入器(<<)向文件输出用析取器(>>)从文件输入。假设file1是以输入方式打开,file2以输出打开。示例如下:

file2<<"I Love You";//向文件写入字符串"I Love You"int i;file1>>i;//从文件输入一个整数值。

这种方式还有一种简单的格式化能力,比如可以指定输出为16进制等等,具体的格式有以下一些

操纵符 功能 输入/输出 

  • dec 格式化为十进制数值数据 输入和输出 
  • endl 输出一个换行符并刷新此流 输出 
  • ends 输出一个空字符 输出 
  • hex 格式化为十六进制数值数据 输入和输出 
  • oct 格式化为八进制数值数据 输入和输出 
  • setpxecision(int p) 设置浮点数的精度位数 输出

比如要把123当作十六进制输出:

file1<<hex<<123;

要把3.1415926以5位精度输出:

file1<<setpxecision(5)<<3.1415926

来源: <http://hi.baidu.com/xpayt/item/b2fa4b487ce641eea4c06661>
 
  3.MFC中 用CFile类,哈哈!简单好用
文件保存为
    CFileDialog fileDlg(FALSE);        //TRUE:打开对话框; FALSE:保存对话框fileDlg.m_ofn.lpstrTitle="我的文件保存对话框";fileDlg.m_ofn.lpstrFilter="Text Files(*.txt)\0*.txt\0All Files(*.*)\0*.*\0\0";fileDlg.m_ofn.lpstrDefExt="txt";if(IDOK==fileDlg.DoModal()){    CFile file(fileDlg.GetFileName(),CFile::modeCreate | CFile::modeWrite);    file.Write("http://www.sunxin.org",strlen("http://www.sunxin.org"));    file.Close();}


CStdioFile继承自CFile,一个CStdioFile 对象代表一个用运行时函数fopen 打开的C 运行时流文件。

      流式文件是被缓冲的,而且可以以文本方式(缺省)或二进制方式打开。文本方式提供对硬回车—换行符对的特殊处理。当你将一个换行符(0x0A)写入一个文本方式的CStdioFile 对象时,字节对(0x0D,0x0A)被发送给该文件。当你读一个文件时,字节对(0x0D,0x0A)被翻译为一个字节(0x0A)。 
  CStdioFile 不支持DuplicateLockRange,和UnlockRange 这几个CFile 函数。如果在CStdioFile 中调用了这几个函数,将会出现CNoSupported 异常。 
CStdioFile::ReadString(LPTSTR lpsz, UINT nMax); 
  读取一行文本到缓冲区,遇到“0x0D,0x0A”时停止读取,并且去掉硬回车“0x0D”,保留换行符“0x0A”,在字符串末尾添加“/0”(0x00)。nMax个字符里包含0x00这个字符。分析如下: 
  1)如果nMax <= 字符数,读取(nMax-1)个字符 + 0x00 
  2)如果nMax = 字符数 + 1,读取nMax个字符 + 0x00 
  3)如果nMax > 字符数,读取字符数 + 0x0A + 0x00 
CStdioFile::ReadString(CString &rString);(重载) 
  读取一行文本到rString,遇到回车换行符停止读取。回车和换行符不读到rString,而且末尾也没有添加“/0”。 
CStdioFile::WriteString( LPCTSTR lpsz );(不支持CString直接写入) 
  将一个缓冲区中的数据写入与CStdioFile 对象关联的文件中。结束的空字符(“/0”)不被写入该文件。lpsz 中的所有换行符都被以一个硬回车换行符对写入该文件,即“/n”被转化成“/r/n”写入到文件里。 
  CFile的派生类CStdioFile提供了对文件进行流式的操作功能。其中函数void CStdioFile::WriteString( LPCTSTR lpsz )写入一个字符串,需要给字符串lpsz的末尾加上换行标志”/r/n”;函数bool CStdioFile::ReadString(CString &rString )从文件中读取一行,如果文件未读完返回true,否则返回false。   
    
    当CStdioFile.WriteString无法处理中文时,把工程的字符集属性 改成 使用多字节字符集 就可以了,具体设置方法如:打开项目属性-->配置属性-->常规-->字符集-->改成 使用多字节字符集
    要注意的是CStdioFile在_MSBC环境下读取任何ANSI文本数据都没问题,在UNICODE环境下读取ANSI文本中的中文时就会显示乱码。其原因是由于CStdioFile 读取ANSI文本数据时按char类型读取,在_MSBC下可以直接填充到CString,在UNICODE环境下要先将char转换成宽字符 WCHAR,然后再填充到CString,即一个汉字的两个char将变成两个UNICODE字符WCHAR。那么问题如何解决呢?实际上在UNICODE环境下,file.ReadString(strLine)取得的数据是char类型,但是存储在UNICODE字符串中。为了取得真实数据,必须对strLine进行处理。


  在CFile类中提供了2种模式,typeBinary和typeText。如果是CTextFile类的话当然要强制使用typeText类型了。那么CStdioFile是怎么做的呢?
        
BOOL CStdioFile::Open(LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pException){   …   /// 调用基类打开文件,注意通过nOpenFlags & ~typeText过滤了文件的文本属性   if (!CFile::Open(lpszFileName, (nOpenFlags & ~typeText), pException))   return FALSE;   …  ///转换找开模式  if (nOpenFlags & typeBinary)   szMode[nMode++] = 'b', nFlags ^= _O_TEXT;   else   szMode[nMode++] = 't';   …   // open a C-runtime low-level file handle   int nHandle = _open_osfhandle((UINT_PTR) m_hFile, nFlags);   // open a C-runtime stream from that handle   if (nHandle != -1)   m_pStream = _fdopen(nHandle, szMode);   …}

   由上面的代码可以看出,CStdioFile强制过滤了typeText标志,使得文件打开模式可以是二进制模式。这已经可以证明CStdioFile不是一个纯粹TextFile类。
   再仔细看上面的代码,在调用基类打开后,又使用_open_osfhandle和_fdopen做了奇怪的转换,察看m_pStream的类型 
FILE* m_pStream;// stdio FILE
   这是一个标准I/O的FILE结构。如果再进一步的研究CStdioFile的源码,还会发现它都是调用标准I/O的函数来操作的,比如用_fputts/_fgetts来实现WriteString/ReadString,用fread/fwrite/fseek来替代Read/Write/Seek。注意CStdioFile并没有使用CFile的Read/Write/Seek函数,如果在Open里将使用fopen直接打开的话,CStdioFile甚至可以说和CFile没有任何的关系。,由此这个类叫做CStdioFile就不奇怪了。
   为什么要研究这个问题呢?前一段在csdn.net上看到不少人问为什么UNICODE的文本文件处理出现乱码?他们很惊异发现平常作为TextFile的处理类用得CStdioFile竟然失效了。真的是它失效了吗?其实是标准的I/O操作并不支持UNICODE的模式而已。
  4.利用win32 API函数 CreateFile(),及WriteFile().
0 0
原创粉丝点击