MiniNote1.0代码分析之文本存取器
原文:
http://www.mini188.com/showtopic-1148.aspx(原文中有pdf版本可以下载)
一、 概述文本存取器(M8NoteFileReader)是MiniNote1.0的核组件,承担了主要的存储工作。在1.0中文本存取器实现了ANSI、Uncode两种编码格式的文本文件读写。
在开发M8NoteFileReader的过程中也是经过了多个版本,最初MiniNote是采用的是ini文件实现数据的保存,但最后发现ini文件“键=值”形式结构文件,无法支持换行符。后来才采用了txt文件代替。在此也告诉一些新手朋友们,不要用ini文件存取长文本,ini用于软件参数配置的存储是非常好的。
二、 功能说明(方法)M8NoteFileReader功能非常简单,既然叫文本存取器,也就是说主要就是“保存”和“读取”。类及方法申明请查看源代码,在此就不再贴出来。
既然是读取文件当然要知道文件的位置,对于M8NoteFileReader来说,它并关心如何取得文件的位置,只需要调用者将文件位置传入即可。M8NoteFileReader提供了一个OpenFile方法用于打开一份文本文件,方法申明如下:
- virtual bool OpenFile(TCHAR *filename);
复制代码参数filename就是需要外部调用者传入的文本文件位置。在OpenFile方法中进行了哪些处理尼?
- //打开文本文件
- bool M8NoteFileReader::OpenFile(TCHAR *filename)
- {
- m_TextCode = ttcUnicode;//默认为unicode
- m_FileName = filename;
- GetTextCode(m_FileName);
- if (m_TextCode == ttcUnicode)
- {
- ReadFileByUnicode();
- }
- else if (m_TextCode == ttcAnsi)
- {
- ReadFileByAnsi();
- }
- return true;
- }
复制代码方法中主要分为二部分:初始化变量、根据编码类型读取文件。
进入到OpenFile后,第一步进行了变量的初始化,即OpenFile的前三行代码。前两行就不解释了,直接看GetTextCode(m_FileName),这句代码是用于获取文件的编码格式,进入到GetTextCode的代码中:
- void M8NoteFileReader::GetTextCode(TCHAR *filename)
- {
- if (FileExists(filename))
- {
- fstream openfile;//创建文件流对象
- openfile.open(filename, ios::in | ios::binary);//加载文件
- openfile.seekg(0, ios::beg);//移动到首字节
- char *code = new char[2];//创建一个两字节的字符串存放BOM
- openfile.read(code, 2);//从流中读取前两个字节到字符串中
- openfile.close();//关闭流对象,释放文件资源
-
- unsigned char cc = (unsigned char)code[0];//将BOM的第一个字节转换为一个byte
- unsigned char cc1 = (unsigned char)code[1]; //将BOM的第二个字节转换为一个byte
- if ((cc == 0xFF) && (cc1 == 0xFE))
- {
- m_TextCode = ttcUnicode;
- }
- else if ((cc == 0xFE) && (cc1 == 0xFF))
- {
- m_TextCode = ttcUnicodeBigEndian;
- }
- else if ((cc == 0xEF) && (cc1 == 0xBB))
- {
- m_TextCode = ttcUnicode;
- }
- else
- {
- m_TextCode = ttcAnsi;
- }
- }
- }
复制代码在代码最开始就判断了文件是否存在,这是参数有效的验证,如果存在则会创建一个fstream对象,这个对象是用于处理流的对象,fstream可以将文件以二进制流的形式进行读取,程序员便可以方便的进行各种操作了。
在openfile.open(filename, ios::in | ios::binary)这句代码就是将文件加载到流的过程,其中第二参数很重要,ios::in表示只读,ios::binary表示二进制形式,还有其他类型具体请查看MSDN。
Open成功后应该有返回值,在这个程序中没有对返回值做检查这是一个问题,如果出现返回失败的情况接下来的代码就有可能报错,所以在注意了。
接着将文件的前两个字节读取出来,这两个字节就是BOM结构啦。所谓BOM结构就是用于描述文本文件的编码规格的,这样程序在读取时就能使用正确的编码解析文件了。BOM结构的说明请查看《》,在上面的代码中可以看到,对BOM结构进行判断后就可以设置编码类型了。可能到这了会一个疑问,这个m_TextCode是个什么东西?这个是我申明的一个枚举,如下:
- //字符编码类型
- enum TextTypeCode{ttcAnsi, ttcUnicode, ttcUnicodeBigEndian, ttcUtf8};
复制代码将编码的类型列为枚举这样在编程时易于阅读及修改。
好了,回到OpenFile这个方法中,在获取到编码类型后就可以针对不同类型的文件进行读取解析了。由于MiniNote1.0只支持了Ansi和UniCode两种编码的文本文件,所以在if结构就只有两个分支。
第一个分支是Unicode,为什么放在前面呢?因为wince中默认是unicode,所以大部分文本都是以unicode为主的,进入到这个分支后会调用:ReadFileByUnicode()方法。
ReadFileByUnicode方法是M8NoteFileReader的一个方法,方法名已经说明了它是读取unicode文本文件的。于是进入到方法里面:
- void M8NoteFileReader::ReadFileByUnicode()
- {
- wifstream ofile;
- ofile.open(m_FileName.C_Str(), ios::in | ios ::binary);
- if (ofile.is_open())//是否打开了文件?
- {
- ofile.seekg(0, ios::end);
- int nLen = ofile.tellg();//获取文件的长度
- ofile.seekg(2, ios::beg);//跳过BOM结构
- TCHAR *tmpstr = new TCHAR[nLen];//申请一个字符串用于存放文件内容地
- wmemset(tmpstr, 0, nLen);//初始化置0
- ofile.read(tmpstr, nLen);//读取文件内容到字符串中
- tmpstr[nLen] = '\0\0';//加上字符串结束符
- m_Text = tmpstr;//将读取的内容写入类成员中,供外部访问
- delete [] tmpstr; //清理字符串
- }
- ofile.close();//释放文件资源
- }
复制代码每行代码我都添加了注释以便于理解,在这里着重说明一个wifstream这个类,wifstream是fstream的一个子类,多了wi是啥意思呢?w表示wide就是指宽字符,因为unicode是双字节的都叫宽字符。I指的是啥我还真不知道,但是前面在讲fstream的open方法时提到了ios::in,大概和这个意思差不多。就是读取的意思。这下就清楚了,wifstream就是用于读取宽字符的类。因为这个宽字符的读取可是折腾了我好一阵子。
说完了ReadFileByUnicode,再回到OpenFile方法里,接着看另一个分支ReadFileByAnsi,代码如下:
- void M8NoteFileReader::ReadFileByAnsi()
- {
- //读取文本
- ifstream file1;
- file1.open(m_FileName.C_Str(), ios::in | ios::binary);
- if (file1.is_open())
- {
- file1.seekg(0, ios::end);
- int nLen = file1.tellg();
- char *ss = new char[nLen+1];
- memset(ss, 0, nLen);
- file1.seekg(0, ios::beg);
- file1.read(ss, nLen);
- ss[nLen] = '\0';
- m_Text = chr2wch(ss);
- }
- file1.close();
- }
复制代码代码和unicode的方法类似,区别在于ifstream和wifstream上,嗯嗯。这两个除了名字外还有啥具体区别呢?呵呵,前面说了unicode是宽字符,而Ansi文本文件中的字符都是单字节的。其实说白了宽字符就是相对于char来说的,就是指widechar比char宽一点。使用ifstream读取文件对于Ansi文本,过程与wifstream类似,只是在m_Text赋值时有一点特别的处理:chr2wch(ss)。这是在干啥呢?chr2wch就是将char字符串转化widechar字符串,汗!~~整了半天还是要换成widestring。没办法在wince中默认是unicode的,而且MiniNote中用于显示文件内容的控件都是使用unicode的,只好转换过去。
好啦,文件读取成功啦。调用M8NoteFileReader.GetText()就能读到内容了。
讲完了读取过程接下来讲保存过程,既然打开是OpenFile方法,那保存自然就叫SaveFile了,直接进入代码吧:
- //保存文本文件
- bool M8NoteFileReader::SaveFile(TCHAR *filename, TCHAR *context)
- {
- if (m_TextCode == ttcUnicode)
- {
- SaveFileByUnicode(filename, context);
- }
- else if (m_TextCode == ttcAnsi)
- {
- SaveFileByAnsi(filename, context);
- }
- return true;
- }
复制代码SaveFile有两个参数,第一个参数是待保存的文件位置,第二参数就是文件内容。保存过程只有一个if语句,同样的也只有unicode和ansi两种格式,这个有一点要注意,if语句里的m_TextCode是在保存方法之外设置的,MiniNote中是使用的读取时的文件编码。
进入到Unicode文件的保存方法SaveFileByUnicode里:
- //保存Unicode文本
- void M8NoteFileReader::SaveFileByUnicode(TCHAR *filename, TCHAR *context)
- {
- wofstream outFile;
- outFile.open(filename, ios::out | ios::binary);//打开并设置为写入
- if (outFile.is_open())
- {
- outFile.seekp(0, ios::beg);//定位到文件首部
- //先写入bom结构
- TCHAR bom = 0xfeff;
- outFile.write(&bom, 1);
- //再写入文本内容
- int nLen = wcslen(context);//获取文本内容的长度
- outFile.write(context, nLen);//写入内容
- }
- outFile.close();
- }
复制代码与读取时一样写入文件采用的类wofstream也是针对widechar的,保存过程多了一个步骤用于写入BOM结构,这样保存的文件就会有BOM结构,否则读出来会乱码。
返回到SaveFile方法里再看SaveFileByAnsi方法:
- //保存Ansi文本
- void M8NoteFileReader::SaveFileByAnsi(TCHAR *filename, TCHAR *context)
- {
- ofstream outFile;
- outFile.open(filename, ios::out | ios::binary);
- if (outFile.is_open())
- {
- char * ss = wch2chr(context);//要将文件内容转换为char字符串
- int nLen = strlen(ss);
- outFile.seekp(0, ios::beg);
- outFile.write(ss, nLen);
- }
- outFile.close();
- }
复制代码写入ansi文本使用的类是ofstream,对于ansi文本不需要写BOM结构。但是保存方法里传入的是TCHAR类型的字符串,而ANSI是char字符串,所以保存前要转context转换为char类型字符串,wch2chr就是干这个活的。
篇结至此保存也说完了,看来很简单嘛,但对于不知道的人来说要研究上一小段时间才能整理出来,我就是查资料问人整了一个月才稳定下来(其实就用了其中的一点点时间,嘿嘿),最后才形成这个M8NoteFileReader。对于很多需要读写文本文件的朋友来说,这个类还是很有用的,直接拿去就能使用。哦,对了,要使用MZFC的程序咯。
其实M8NoteFileReader这个类还是写的比较差的,虽然基本的功能都已经实现,但是结构上不是很舒服,如果闲的没事重构一下,比如提取一个虚类,这样对于unicode和ansi分别继承一个子类,这样以后有了新的文本类型需要处理只要实现新的子类就好了。对于调用者来说没有什么影响。嗯嗯,以后再说吧。
本文的pdf
- MiniNote1代码分析之文本存取器.pdf (, 下载次数:298)