File IO
三种读写方式
1.CFile
CFile file;
CFileException eErr;
CString strFileName = "f://txt1.wkb";
LPCTSTR lpszFileName = strFileName;
try
{
if(!file.Open(lpszFileName, CFile::modeReadWrite | CFile::modeCreate, &eErr))
{
return ;
}
CString strbuf = "hello";
file.Write(strbuf,strbuf.GetLength());
strbuf = "";
file.SeekToBegin();
int iFileLen = file.GetLength();
file.Read( strbuf.GetBuffer(iFileLen),iFileLen);
strbuf.ReleaseBuffer();
file.Close();
}
catch (CFileException* e)
{
AfxMessageBox("Operating the file failed.");
}
2.API
char * pchTxtFileName="f://txt.txt";
HANDLE hFile=::CreateFile(pchTxtFileName,GENERIC_WRITE | GENERIC_READ,FILE_SHARE_READ,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
CString strTmp = "hello";
DWORD dNumOfAccess;
::WriteFile(hFile,strTmp,strTmp.GetLength(),&dNumOfAccess,NULL);
int nLength=::SetFilePointer(hFile,0,NULL,FILE_END);
char *pData=new char [nLength+1];
memset(pData,0,nLength+1);
::SetFilePointer(hFile,0,NULL,FILE_BEGIN);
::ReadFile(hFile,pData,nLength,&dNumOfAccess,NULL);
delete[] pData;
CloseHandle(hFile);
3.FILE
char *pDt = new char [100];
memset(pDt,'a',3);
pDt[3] = '/0';
FILE * pFile=fopen(pchTxtFileName,"wt+");
fprintf(pFile,"%s/r/n",pDt);
memset(pDt,0,3);
fseek(pFile,0,SEEK_SET);
fscanf(pFile,"%s/r/n",pDt);
delete [] pDt;
fclose(pFile);
二进制文件和文本文件
介绍
大家都知道计算机的存储在物理上是二进制的,所以文本文件与二进制文件的区别并不是物理上的,而是逻辑上的。这两者只是在编码层次上有差异。
简单来说,文本文件是基于字符编码的文件,常见的编码有ASCII编码,UNICODE编码等等。二进制文件是基于值编码的文件,你可以根据具体应用,指定某个值是什么意思(这样一个过程,可以看作是自定义编码)。
从上面可以看出文本文件基本上是定长编码的,基于字符嘛,每个字符在具体编码中是固定的,ASCII码是8个比特的编码,UNICODE一般占16个比特。而二进制文件可看成是变长编码的,因为是值编码嘛,多少个比特代表一个值,完全由你决定。大家可能对BMP文件比较熟悉,就拿它举例子吧,其头部是较为固定长度的文件头信息,前2字节用来记录文件为BMP格式,接下来的8个字节用来记录文件长度,再接下来的4字节用来记录bmp文件头的长度。。。大家可以看出来了吧,其编码是基于值的(不定长的,2、4、8字节长的值都有),所以BMP是二进制文件。
二进制文件把内存中的数据按其在内存中存放形式原样输出到磁盘上。
文本文件:把文件看作是一个一个的字符序列。
读写
可用FILE * pFile=fopen(pchTxtFileName,"wt+");中的第二个参数决定读写二进制文件还是文本文件。具体可以查看MSDN。
CFile一般是读写文件文件。
写Ansi文本文件和Unicode文本文件
Anis文本文件
一般常见的是Ansi文本文件,例如上面的三种读写方式的写文件方式。
Unicode文本文件
//写Unicode文本文件,头个字节xfeff,低位xff写在前
CFile file;
file.Open(_T("1.txt"), CFile::modeWrite | CFile::modeCreate);
//文件开头
file.SeekToBegin();
// 决定是Ansi文件还是Unicode文件
file.Write("/xff/xfe", 2); // 没有这一句,则为Ansi文件,这里则表示Unicode文件
//写入内容
file.Write("abc", 3);
file.Flush();
file.Close();
按行读取文本,CStdioFile扩展类:CStdioFileEx
支持宽字节、多字节环境;支持Ansi、Unicode文本;支持中英文。
最初,直接从网上下了CStdioFileEx类使用,发现好像并不好用。于是自己决定重写。于是花了一天时间,在各种环境下查看微软是如何读文本的。刚开始一直被Unicode文本的读取方式迷惑,知道最后才发现,要用二进制方式读取,否则自动把每一个字节扩展成2个字节。这就是造成乱码的根本原因。查找到原因,就好写了。当然是把CStdioFile的代码copy过来,根据正确环境,用正确的函数。结果发现,网上也是这种方式。虽然解决方法都是一样,但习惯或者思维方式不一样,所以才觉得他的类并不好用。总是觉得,他考虑的有点过于复杂,把简单问题复杂化了。所以按照自己的思路,完完整整的,一字一句的写下了自己的类。
有时,快乐本身,就是一种过程,自己的过程。也许尽管是别人走过的过程,但却属于自己的过程,不一样的过程。
好了,废话不多说。源码如下:
ini配置文件读写
在Windows中我们可以使用注册表或者 ini 文件(Profile)来保存少量数据,如程序的初始化信息,用户设置的数据等。
通过应用程序,写注册表
AfxGetApp()->WriteProfileString(sPath,sKeyPath,m_sPath);
AfxGetApp()->GetProfileString(sFilePath,sKeyFilePath,"");
ini文件
ini文件(即Initialization file),由若干个节(Section)组成,每个Section由若干键(Key)组成,每个Key可以赋相应的值。读写ini文件实际上就是读写某个的Section中相应的Key的值。
读写ini文件函数
GetPrivateProfileString 读取INI文件指定块中的键名对应的字符串。
GetPrivateProfileInt 读取INI文件指定块中的键名对应的整数值。
GetPrivateProfileStruct 读取INI文件指定块中的键名对应的数据
GetPrivateProfileSection 读取INI文件指定块中的所有键名及其对应值。
GetPrivateProfileSectionNames 读取一INI文件中所有的块名。
GetProfileInt 读取win.ini中指定块中的键名对应的整数值。
GetProfileString 读取win.ini中指定块中的键名的对应值。
GetProfileSection 读取win.ini中指定块中所有的键名及其值。
(win.ini,是Windows系统的一个基本系统配置文件。在C:/Windows下,故用GetProfileInt等函数时,不需要指定路径。但不推荐保存私有消息。)
WritePrivateProfileSection 替换INI文件中指定块中所有键名对应的值。
WritePrivateProfileString 把给定的键名及其值写入到指定INI文件的相应块中。
WritePrivateProfileStruct 把指定的键名及其数据写入到指定INI文件的块中。
WriteProfileSection 替换win.ini中指定块的所有键名对应的值。
WriteProfileString 将给定的键名及值写入win.ini中对应的块中。
例子
// --------------------------------------------------------------------------------------------
// [Section1]
// Key1=1
// Key2=2
// Key3=3
//
// [Section2]
// Key4=4
// Key5=5
// Key6=6
// --------------------------------------------------------------------------------------------
#define MAX_COUNT 1000
CString iniFile(_T(".//1.ini"));
// 读取指定块中的键名对应的字符串。
CString val;
GetPrivateProfileString(_T("Section1"), _T("Key1"), _T(""), val.GetBuffer(MAX_COUNT), MAX_COUNT, iniFile);
val.ReleaseBuffer(); // val = "1"
// 读取指定块中的键名对应的整数。
int nVal = GetPrivateProfileInt(_T("Section1"), _T("Key1"), 0, iniFile); // nVal = 1
// 把Key2=2 改成Key2=222
WritePrivateProfileString(_T("Section1"), _T("Key2"), _T("222"), iniFile);
删除键值或节
回顾一下WriteProfileString函数的说明
BOOL WriteProfileString(
LPCTSTR lpAppName, // 节的名字,是一个以0结束的字符串
LPCTSTR lpKeyName, // 键的名字,是一个以0结束的字符串。若为NULL,则删除整个节
LPCTSTR lpString // 键的值,是一个以0结束的字符串。若为NULL,则删除对应的键
)
由此可见,要删除某个节,只需要将WriteProfileString第二个参数设为NULL即可。而要删除某个键,则只需要将该函数的第三个参数设为 NULL即可。这是删除系统的win.ini中的节或键,类似的,要删除自己定义的ini文件中的节或键,也可做相同的操作。
如:
::WriteProfileString("Test",NULL,NULL); //删除win.ini中的Test节
::WriteProfileString("Test","id",NULL); //删除win.ini中的id键
::WritePrivateProfileString("Test",NULL,NULL,".//ex1.ini"); //删除ex1.ini中的Test节
::WritePrivateProfileString("Test","id",NULL,".//ex1.ini"); //删除ex1.ini中的id键
读取ini文件所有的节名
要判断一个ini文件中有多少个节,最简单的办法就是将所有的节名都找出来,然后统计节名的个数。而要将所有的节名找出来,使用GetPrivateProfileSectionNames函数就可以了,其原型如下:
DWORD GetPrivateProfileSectionNames(
LPTSTR lpszReturnBuffer, // 指向一个缓冲区,用来保存返回的所有节名
DWORD nSize, // 参数lpszReturnBuffer的大小
LPCTSTR lpFileName // 文件名,若该ini文件与程序在同一个目录下,
//也可使用相对路径,否则需要给出绝度路径
)
/* ------------------------------------------------------------------------------------------------
Function Name : GetSectionNames
Describe : 读取ini文件中所有的节名
Parameters :
CStringArray& sections ----- [out] 所有节名字符串数组
LPCTSTR lpIniFile ----- [in] ini文件路径
Return type : int ----- 节名个数
------------------------------------------------------------------------------------------------
I D :
Others :
------------------------------------------------------------------------------------------------
Author : Alsmile Date : 2010-8-12
Project :
Version : 1.0.0.1
Module :
------------------------------------------------------------------------------------------------
History :
/* --------------------------------------------------------------------------------------------- */
int GetSectionNames(CStringArray& sections, LPCTSTR lpIniFile)
{
const int MaxStringCount = 3000;
int count(0);
try
{
// 所有节名字符串数组
TCHAR szSectionNames[MaxStringCount] = {0};
int length = GetPrivateProfileSectionNames(szSectionNames, MaxStringCount, lpIniFile);
int index(0);
for(int i = 0; i < length; ++i)
{
if(szSectionNames[i] == '/0')
{
sections.Add(&szSectionNames[index]);
TRACE(sections[count]);
++count;
index = i + 1;
//当两个相邻的字符都是时,则所有的节名都已找到,循环终止
if(szSectionNames[index] == 0)
{
break;
}
}
}
}
catch (...)
{
}
return count;
}
读取ini文件中指定节下面的所有键名和键值
/* ------------------------------------------------------------------------------------------------
Function Name : GetSectionKeysValues
Describe : 读取ini文件中指定节下的所有键名和键值
Parameters :
CStringArray& keys ----- [out] 所有键名字符串数组
CStringArray& vals ----- [out] 所有键值字符串数组
LPCTSTR lpSection ----- [in] 指定节名
LPCTSTR lpIniFile ----- [in] ini文件路径
Return type : int ----- 键个数
------------------------------------------------------------------------------------------------
I D :
Others :
------------------------------------------------------------------------------------------------
Author : Alsmile Date : 2010-8-12
Project :
Version : 1.0.0.1
Module :
------------------------------------------------------------------------------------------------
History :
/* --------------------------------------------------------------------------------------------- */
int GetSectionKeysValues(CStringArray& keys, CStringArray& vals, LPCTSTR lpSection, LPCTSTR lpIniFile)
{
const int MaxStringCount = 6000;
int count(0);
TRACE(_T("/n"));
try
{
// 包括key和value的总字符串
TCHAR szKeysVals[MaxStringCount] = {0};
int length = GetPrivateProfileSection(lpSection, szKeysVals, MaxStringCount, lpIniFile);
int index(0);
for(int i = 0; i < length; ++i)
{
if (szKeysVals[i] == '/0')
{
if (i != index)
{
CString lineText(&szKeysVals[index]);
keys.Add(lineText.Left(lineText.Find('=')));
TRACE(keys[count] + _T("="));
vals.Add(lineText.Mid(lineText.Find('=') + 1));
TRACE(vals[count] + _T("/n"));
++count;
}
index = i + 1;
}
}
}
catch (...)
{
}
return count;
}
xml文件读写
推荐使用 tinyXML(使用简单)或 CMarkup
tinyXML:tinyxml使用笔记与总结 (请自己网上google)
CMarkup:CMarkup7.2 概述 CMarkUp函数简介 (请自己网上google这两个文章)