MFC提供了CFile类方便文件的读写,首先要知道,文件的数据读取、数据写入与文件指针的操作都是以字节为单位的,数据的读取和写入都是从文件指针的位置开始的,当打开一个文件的时候,文件指针总是在文件的开头。常规方法如下:
CFile file;
file.open( LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError = NULL );
//pszFileName是文件名,可包含文件路径,若只有文件名,则默认路径为工程路径,nOpenFlags是文件打开模式,pError是打开失败时用来接收失败信息,一般设置为NULL。
nOpenFlags的常用模式有:
表1-1 CFile文件打开方式 |
|
标志 |
含义 |
CFile::modeCreate |
创建新文件,如果文件已存在,则将其长度变成0 |
CFile::modeNoTruncate |
与modeCreate组合使用,如果文件已存在,则不会将其长度变成0 |
CFile::modeRead CFile::modeReadHuge |
以只读方式打开文件 Can read more than 64K of (unbuffered) data from a file at the current file position. Obsolete in 32-bit programming. |
CFile::modeReadWrite |
以读写方式打开文件 |
CFile::modeWrite |
以只写方式打开文件 |
CFile::modeNoInherit |
组织该文件被子项继承 |
CFile::shareDenyNone |
以共享模式打开文件,不会禁止其他进程对文件的读写 |
CFile::shareDenyRead |
禁止其他进程对文件的读操作 |
CFile::shareDenyWrite |
禁止其他进程对文件的写操作 |
CFile::shareExclusive |
以独占模式打开文件,禁止其他进程对文件的读写 |
CFile::typeText |
以文本方式打开文件 |
CFile::typeBinary |
以二进制方式打开文件 |
CFile::modeCreate 若打开文件不存在,则创建一个新文件,如果该文件存在,则清空它的数据。
CFile::modeNoTruncate 与CFile::modeCreate 组合使用。如果文件不存在,则创建一个新文件,如果文件存在,则保留他原本的数据。
CFile::modeRead 打开文件用于读取数据。
CFile::modeWrite 打开文件用于写入数据。
CFile::modeReadWrite 打开文件用于读取和写入数据。
CFile::typeBinary 使用二进制文件模式。此模式仅用于CFile的派生类。
CFile::typeText 使用文本文件模式。此模式仅用于CFile的派生类。
CFile默认使用二进制模式读写文件,CFile无法使用文本模式读写文件。
CFile提供了一些常用的操作函数,如表1-2所示。
表1-2 CFile操作函数 |
|
函数 |
含义 |
Open |
打开文件 |
Close |
关闭文件 |
Flush |
刷新待写的数据 |
Read |
从当前位置读取数据 |
Write |
向当前位置写入数据 |
GetLength |
获取文件的大小 |
Seek |
定位文件指针至指定位置 |
SeekToBegin |
定位文件指针至文件头 |
SeekToEnd |
定位文件指针至文件尾 |
GetFileName |
获取文件名,如:“NOTEPAD.EXE” |
GetFilePath |
获取文件路径,如:“C:\WINDOWS \NOTEPAD.EXE” |
GetFileTitle |
获取文件标题,如:“NOTEPAD” |
GetPosition |
获取当前文件指针 |
GetStatus |
获取当前文件的状态,返回一个CFileStatus |
#Remove |
静态方法,删除指定文件 |
#Rename |
静态方法,重命名指定文件 |
注意最后两个静态函数,其实它们封装了Windows API中关于文件管理的函数。
使用CFile操作文件的流程如下:
构造一个CFile对象。
调用CFile::Open()函数创建、打开指定的文件。
调用CFile::Read()和CFile::Write ()进行文件操作。
调用CFile::Close()关闭文件句柄。
文件指针的位置设置可以使用:
Seek( LONG lOff, UINT nFrom ) 把文件指针移动到指定位置
lOff :是指针偏移字节数,若向后偏移则为正数,若向前偏移,则为负数。
nFrom :MSDN上有三种取值:
CFile::begin 从文件开头开始算起,lOff为正数;
CFile::current 当前位置开始算起;
CFile::end 从文件结尾开始算起,lOff为负数;
SeekToBegin( ) 把文件指针移到文件开头
SeekToEnd( ) 把文件指针移到文件末尾
GetPosition( ) 返回当前文件指针的位置
获取文件的字节数可用 GetLength( ) 此函数的返回值为DWORD,但可直接用来分配数组元素数目,例如:
DOWRD len=file.GetLength();
char *pBuf=new char[len+1] / int *pBuf=new int[len/4]
char占一个字节,int占四个字节。
写入文件:
CFile file;
file.Open("E:\\VC\\1.txt",CFile::modeCreate|CFile::modeWrite|CFile::modeNoTruncate,NULL);
file.Write("HelloWorld",strlen("HelloWorld"));
//Write( const void* lpBuf, UINT nCount ) lpBuf是写入数据的Buf指针,nCount是Buf里需要写入文件的字节数
file.close( );
读取文件:
CFile file;
file.Open("E:\\VC\\1.txt",CFile::modeRead,NULL);
DWORD len=file.GetLength( );
char Buf[len+1];
Buf[len]=0; //0终止字符串,用于输出。
file.Read(Buf,len); //Read( void* lpBuf, UINT nCount ) lpBuf是用于接收读取到的数据的Buf指针nCount是从文件读取的字节数
MessageBox(Buf);
一、各种关于文件的操作在程序设计中是十分常见,如果能对其各种操作都了如指掌,就可以根据实际情况找到最佳的解决方案,从而在较短的时间内编写出高效的代码,因而熟练的掌握文件操作是十分重要的。本文将对Visual C++中有关文件操作进行全面的介绍,并对在文件操作中经常遇到的一些疑难问题进行详细的分析。
1.文件的查找
当对一个文件操作时,如果不知道该文件是否存在,就要首先进行查找。MFC中有一个专门用来进行文件查找的类CFileFind,使用它可以方便快捷地进行文件的查找。下面这段代码演示了这个类的最基本使用方法。
CString strFileTitle;
CFileFind finder;
BOOL bWorking = finder.FindFile('C:\\windows\\sysbkup\\*.cab');
while(bWorking)
{
bWorking=finder.FindNextFile();
strFileTitle=finder.GetFileTitle();
}
2.文件的打开/保存对话框 CFileDialog mFileDlg(TRUE,NULL,NULL,
OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT|OFN_ALLOWMULTISELE CT,
'All Files (*.*)|*.*||',AfxGetMainWnd());
CString str(' ',10000);
mFileDlg.m_ofn.lpstrFile=str.GetBuffer(10000);
str.ReleaseBuffer();
POSITION mPos=mFileDlg.GetStartPosition();
CString pathName(' ',128);
CFileStatus status;
while(mPos!=NULL)
{
pathName=mFileDlg.GetNextPathName(mPos);
CFile::GetStatus( pathName, status );
}
3.文件的读写 char sRead[2];
CFile mFile(_T('user.txt'),CFile::modeRead);
if(mFile.GetLength()<2)
return;
mFile.Read(sRead,2);
mFile.Close();
//对文件进行写操作
CFile mFile(_T('user.txt '), CFile::modeWrite|CFile::modeCreate);
mFile.Write(sRead,2);
mFile.Flush();
mFile.Close();
虽然这种方法最为基本,但是它的使用繁琐,而且功能非常简单。我向你推荐的是使用CArchive,它的使用方法简单且功能十分强大。首先还是用CFile声明一个对象,然后用这个对象的指针做参数声明一个CArchive对象,你就可以非常方便地存储各种复杂的数据类型了。它的使用方法见下例。 //对文件进行写操作
CString strTemp;
CFile mFile;
mFile.Open('d:\\dd\\try.TRY',CFile::modeCreate|CFile::modeNoTruncate|CFile::modeWrite);
CArchive ar(&mFile,CArchive::store);
ar<< ar.Close();
mFile.Close();
//对文件进行读操作
CFile mFile;
if(mFile.Open('d:\\dd\\try.TRY',CFile::modeRead)==0)
return;
CArchive ar(&mFile,CArchive::load);
ar>>strTemp;
ar.Close();
mFile.Close();
CArchive的 << 和>> 操作符用于简单数据类型的读写,对于CObject派生类的对象的存取要使用ReadObject()和WriteObject()。使用CArchive的ReadClass()和WriteClass()还可以进行类的读写,如: //存储CAboutDlg类
ar.WriteClass(RUNTIME_CLASS(CAboutDlg));
//读取CAboutDlg类
CRuntimeClass* mRunClass=ar.ReadClass();
//使用CAboutDlg类
CObject* pObject=mRunClass->CreateObject();
((CDialog* )pObject)->DoModal();
虽然VC提供的文档/视结构中的文档也可进行这些操作,但是不容易理解、使用和管理,因此虽然很多VC入门的书上花费大量篇幅讲述文档/视结构,但我建议你最好不要使用它的文档。关于如何进行文档/视的分离有很多书介绍,包括非常著名的《Visual C++ 技术内幕》。 CStdioFile mFile;
CFileException mExcept;
mFile.Open( 'd:\\temp\\aa.bat', CFile::modeWrite, &mExcept);
CString string='I am a string.';
mFile.WriteString(string);
mFile.Close();
4.临时文件的使用 char szTempPath[_MAX_PATH],szTempfile[_MAX_PATH];
GetTempPath(_MAX_PATH, szTempPath);
GetTempFileName(szTempPath,_T ('my_'),0,szTempfile);
CFile m_tempFile(szTempfile,CFile:: modeCreate|CFile:: modeWrite);
char m_char='a';
m_tempFile.Write(&m_char,2);
m_tempFile.Close();
5.文件的复制、删除等 CString FilePathName;
CFileDialog dlg(TRUE);///TRUE为OPEN对话框,FALSE为S***E AS对话框
if (dlg.DoModal() == IDOK)
FilePathName=dlg.GetPathName();
相关信息:CFileDialog 用于取文件名的几个成员函数:{
CFile file;
file.Open('C:\HELLO.TXT',CFile::modeCreate|Cfile::modeWrite);
.
.
.
}
[3]移动文件指针BOOL CloseHandle(HANDLE hObject // handle to object to close);
例子1、在当前目录下面创建一个文件: HANDLE handle;
DWORD Num;
handle= ::CreateFile('new.tmp',GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_ALWAYS,FILE_FLAG_DELETE_ON_CLOSE,NULL);
if(INVALID_HANDLE_VALUE!= handle )
{
::SetFilePointer(handle,0,0,FILE_BEGIN);
char Buffer[] = '这是个刚创建的文件';
::WriteFile(handle,Buffer,sizeof(Buffer),&Num,NULL);
ZeroMemory(Buffer,sizeof(Buffer));
::SetFilePointer(handle,0,0,FILE_BEGIN);
::ReadFile(handle,Buffer,sizeof(Buffer),&Num,NULL);
MessageBox(Buffer);
::CloseHandle(handle);
}
可以改变上面的创建文件的属性和操作看下不同效果。 CFile( LPCTSTR lpszFileName, UINT nOpenFlags );
throw( CFileException );
CFile( );
BOOL Open( LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError = NULL ); char* pFileName = 'test.dat';
TRY
{
CFile f( pFileName, CFile::modeCreate | CFile::modeWrite );
}
CATCH( CFileException, e )
{
#ifdef _DEBUG
afxDump << 'File could not be opened ' << e->m_cause << '\n';
#endif
}
END_CATCH
CFile fileTest;
char* pFileName = 'test.dat';
TRY
{
fileTest.Open(pFileName, CFile::modeCreate |CFile::modeWrite);
}
CATCH_ALL(e)
{
fileTest.Abort( );
THROW_LAST ( );
}
END_CATCH_ALL
2.文件的读写定位 DWORD SetFilePointer(
HANDLE hFile, //文件的句柄
LONG lDistanceToMove, //字节偏移量r
PLONG lpDistanceToMoveHigh, //指定一个长整数变量,其中包含了要使用的一个高双字偏移(一般用来操作大型文件)。可设为零,表示只使用lDistanceToMove
DWORD dwMoveMethod //文件定位
);
dwMoveMethod文件定位的方式有三种: LONG Seek(LONG lOff,UINT nFrom);
throw(CFileException);
如果要求的位置合法,则Seek返回从文件开始起的新字节偏移量 CFile file;
BITMAPINFOHEADER bmpinfo;
try
{
file.Open('D:\ToolBar.bmp',CFile::modeRead);
file.Seek(sizeof(BITMAPFILEHEADER),CFile::begin);
file.Read(&bmpinfo,sizeof(BITMAPINFOHEADER ));
CString str;
str.Format('位图文件的长是%d,高%d',bmpinfo.biWidth,bmpinfo.biHeight);
MessageBox(str);
file.Close();
}
catch(CFileException *e)
{
CString str;
str.Format('读取数据失败的原因是:%d',e->m_cause);
MessageBox('str');
file.Abort();
e->Delete();
}
3.取得和设置文件的创建时间、最后访问时间、最后写时间 BOOL GetFileTime(
HANDLE hFile, // 文件句柄
LPFILETIME lpCreationTime, // 创建时间
LPFILETIME lpLastAccessTime, // 最后访问时间
LPFILETIME lpLastWriteTime // 最后写时间
);
BOOL SetFileTime(
HANDLE hFile,
CONST FILETIME *lpCreationTime,
CONST FILETIME *lpLastAccessTime,
CONST FILETIME *lpLastWriteTime
);
typedef struct _FILETIME {
DWORD dwLowDateTime;
DWORD dwHighDateTime;
}FILETIME;
取得三个参数都是FILETIME结构,得到的都是UTC时间,可以通过API函数FileTimeToLocalFileTime()和FileTimeToSystemTime()将他们转换为本地时间和系统时间格式,也可以通过LocalFileTimeToFileTime 和SystemTimeToFileTime()转换回来,通过SetFileTime设置文件的创建时间、最后访问时间、最后写时间。由于使用的时候要先打开文件,而且取得的最后访问时间就是当前时间,没有多大意义,且比较麻烦,下面介绍CFile类中的静态方法。 static BOOL PASCAL GetStatus( LPCTSTR lpszFileName, CFileStatus& rStatus );
static void SetStatus( LPCTSTR lpszFileName, const CFileStatus& status );
throw( CFileException );
返回的是一个CfileStatus对象,这个结构的具体的成员变量包括: struct CFileStatus
{
CTime m_ctime; // 文件创建时间
CTime m_mtime; // 文件最近一次修改时间
CTime m_atime; // 文件最近一次访问时间
LONG m_size; // 文件大小
BYTE m_attribute; // 文件属性
BYTE _m_padding; // 没有实际含义,用来增加一个字节
TCHAR m_szFullName[_MAX_PATH]; //绝对路径
#ifdef _DEBUG
//实现Dump虚拟函数,输出文件属性
void Dump(CDumpContext& dc) const;
#endif
};
下面就举一个例子来实现: CFileStatus status;
char *path = 'D:\VSS';
if(CFile::GetStatus( path, status ))
{
CString cTime,mTime,aTime;
cTime = status.m_ctime.Format('文件建立时间:%Y年%m月%d日 %H时%M分%S秒');
mTime = status.m_mtime.Format('文件最近修改时间:%Y年%m月%d日 %H时%M分%S秒');
aTime = status.m_atime.Format('文件最近访问时间:%Y年%m月%d日 %H时%M分%S秒');
CString str;
str = cTime + '\n' + mTime +'\n' + aTime ;
MessageBox(str);
}
4.取得和设置文件的属性 DWORD GetFileAttributes(
LPCTSTR lpFileName //文件或文件夹路经
);
BOOL SetFileAttributes(
LPCTSTR lpFileName, // 文件名
DWORD dwFileAttributes // 要设置的属性
);
取得的文件属性包括:FILE_ATTRIBUTE_ARCHIVE,FILE_ATTRIBUTE_HIDDEN,FILE_ATTRIBUTE_NORMAL,FILE_ATTRIBUTE_OFFLINE,FILE_ATTRIBUTE_READONLY,FILE_ATTRIBUTE_SYSTEM,FILE_ATTRIBUTE_TEMPORARYenum Attribute {
normal,
readOnly,
hidden,
system,
volume,
directory,
archive
};
可以通过if((status. m_attribute& readOnly) = =FILE_ATTRIBUTE_READONLY)来判断,这里利用另外的API来实现获得文件的详细信息:HANDLE FindFirstFile(
LPCTSTR lpFileName, //文件或文件夹路经r
LPWIN32_FIND_DATA lpFindFileData
);
BOOL FindNextFile(
HANDLE hFindFile,
LPWIN32_FIND_DATA lpFindFileData
);
BOOL FindClose(HANDLE hFindFile );typedef struct _WIN32_FIND_DATA {
DWORD dwFileAttributes; //文件属性
FILETIME ftCreationTime; // 文件创建时间
FILETIME ftLastAccessTime; // 文件最后一次访问时间
FILETIME ftLastWriteTime; // 文件最后一次修改时间
DWORD nFileSizeHigh; // 文件长度高32位
DWORD nFileSizeLow; // 文件长度低32位
DWORD dwReserved0; // 系统保留
DWORD dwReserved1; // 系统保留
TCHAR cFileName[ MAX_PATH ]; // 长文件名
TCHAR cAlternateFileName[ 14 ]; // 8.3格式文件名
}WIN32_FIND_DATA, *PWIN32_FIND_DATA;
也可以利用另外一个函数来取得文件的信息:BOOL GetFileInformationByHandle(
HANDLE hFile, // 文件的句柄
LPBY_HANDLE_FILE_INFORMATION lpFileInformation
);
函数填充的是BY_HANDLE_FILE_INFORMATION结构体:typedef struct _BY_HANDLE_FILE_INFORMATION {
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD dwVolumeSerialNumber; // 文件所在的磁盘的序列号
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD nNumberOfLinks; //链接的数目
DWORD nFileIndexHigh;
DWORD nFileIndexLow;
}BY_HANDLE_FILE_INFORMATION;
下面就举一个例子来实现:HANDLE handle;
WIN32_FIND_DATA find_data;
handle = :: FindFirstFile('D:\VSS',&find_data);
FindClose(handle);
find_data.dwFileAttributes = find_data.dwFileAttributes|FILE_ATTRIBUTE_READONLY;
::SetFileAttributes('D:\VSS',find_data.dwFileAttributes);
在上面的介绍中,除了可以设置文件的属性之外,在操作的过程当中也可以取得文件的其他一些信息,可以根据具体的需要来实现。 virtual CString GetFileName( ) const,
virtual CString GetFileTitle( ) const,
virtual CString GetFilePath( ) const,
virtual DWORD GetLength( ) const;throw( CFileException );
来取得相关信息,如果一个文件的全路经是: c:\windows\write\myfile.wri,则每个函数取得的是: myfile.wri, myfile, c:\windows\write\myfile.wri. GetLength取得文件大小是按字节为单位的。 virtual void SetLength( DWORD dwNewLen );throw( CFileException );
virtual void SetFilePath( LPCTSTR lpszNewName );
来设置文件的长度和路径。 CFile file('Text.txt',CFile::modeReadWrite);
ULONGLONG length;
CString strFilePath;
length = file.GetLength();
length = length + 1024*10;
file.SetLength(length);
file.SetFilePath('D:\Text.txt');
strFilePath = file.GetFilePath();
MessageBox(strFilePath);
file.Close();
最后发现文件的路径变了,但是在D盘下面并没有找到Text.txt,原因是SetFilePath只能指定一个路径给文件,SetFilePath并不能做为移动文件来使用。CFileDialog( BOOL bOpenFileDialog, LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter = NULL, CWnd* pParentWnd = NULL );
各个参数如下: CString FileFilter = '所有文件(*.*)|*.*||';
CFileDialog FileDialog(true,NULL,NULL,OFN_HIDEREADONLY,FileFilter,NULL);
FileDialog.DoModal();
MessageBox(FileDialog.GetFileName());
6.小结
整改自:http://xiaojun123hello.blog.163.com/blog/static/361666562011101933326392/
http://blog.sina.cn/dpool/blog/s/blog_69b9bb050100lelo.html?vt=4