作者:jostree 转载请注明出处 http://www.cnblogs.com/jostree/p/4374404.html
1.读取UTF-8编码文本原理
首先了解UTF-8的编码方式,UTF-8采用可变长编码的方式,一个字符可占1字节-6字节,其中每个字符所占的字节数由字符开始的1的个数确定,具体的编码方式如下:
U-00000000 - U-0000007F: 0xxxxxxx
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
因此,对于每个字节如果起始位为“0”则说明,该字符占有1字节。
如果起始位为“10”则说明该字节不是字符的起始字节。
如果起始为为$n$个“1”+1个“0”,则说明改字符占有$n$个字节。其中$1 \leq n \leq 6$。
因此对于UTF-8的编码,我们只需要每次计算每个字符开始字节的1的个数,就可以确定这个字符的长度。
2.读取GBK系列文本原理
对于ASCII、GB2312、GBK到GB18030编码方法是向下兼容的 ,即同一个字符在这些方案中总是有相同的编码,后面的标准支持更多的字符。
在这些编码中,英文和中文可以统一地处理。区分中文编码的方法是高字节的最高位不为0。
因此我们只需处理好GB18130,就可以处理与他兼容的所有编码,对于GB18130使用双字节变长编码。
单字节部分从 0x0~0x7F 与 ASCII 编码兼容。双字节部分,首字节从 0x81~0xFE,尾字节从 0x40~0x7E以及 0x80~0xFE,与GBK标准基本兼容。
因此只需检测首字节是否小于0x81即可确定其为单字节编码还是双字节编码。
3.C++代码实现
对于一个语言处理系统,读取不同编码的文本应该是最基础的需求,文本的编码方式应该对系统其他调用者透明,只需每次获取一个字符即可,而不需要关注这个文本的编码方式。从而我们定义了抽象类Text,及其接口ReadOneChar,并使两个文本类GbkText和UtfText继承这个抽象类,当系统需要读取更多种编码的文件时,只需要定义新的类然后继承该抽象类即可,并不需要更改调用该类的代码。从而获得更好的扩展性。
更好的方式是使用简单工厂模式,使不同的文本编码格式对于调用类完全透明,简单工厂模式详解请参看:C++实现设计模式之 — 简单工厂模式
其中Text抽象类的定义如下:
1 #ifndef TEXT_H 2 #define TEXT_H 3 #include <iostream> 4 #include <fstream> 5 using namespace std; 6 class Text 7 { 8 protected: 9 char * m_binaryStr; 10 size_t m_length; 11 size_t m_index; 12 public: 13 Text(string path); 14 void SetIndex(size_t index); 15 virtual bool ReadOneChar(string &oneChar) = 0; 16 size_t Size(); 17 virtual ~Text(); 18 }; 19 #endif
Text抽象类的实现如下:
1 #include "Text.h" 2 using namespace std; 3 Text::Text(string path):m_index(0) 4 { 5 filebuf *pbuf; 6 ifstream filestr; 7 // 采用二进制打开 8 filestr.open(path.c_str(), ios::binary); 9 if(!filestr) 10 { 11 cerr<<path<<" Load text error."<<endl; 12 return; 13 } 14 // 获取filestr对应buffer对象的指针 15 pbuf=filestr.rdbuf(); 16 // 调用buffer对象方法获取文件大小 17 m_length=(int)pbuf->pubseekoff(