使用C++读取UTF-8及GBK系列的文本方法及原理
扫描二维码
随时随地手机看文章
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≤n≤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 #include4 #include5 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 #endifView Code
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."<pubseekoff(0,ios::end,ios::in);
18 pbuf->pubseekpos(0,ios::in);
19 // 分配内存空间
20 m_binaryStr = new char[m_length+1];
21 // 获取文件内容
22 pbuf->sgetn(m_binaryStr,m_length);
23 //关闭文件
24 filestr.close();
25 }
26
27 void Text::SetIndex(size_t index)
28 {
29 m_index = index;
30 }
31
32 size_t Text::Size()
33 {
34 return m_length;
35 }
36
37 Text::~Text()
38 {
39 delete [] m_binaryStr;
40 }View Code
GBKText类的定义如下:
#ifndef GBKTEXT_H
#define GBKTEXT_H
#include#include#include "Text.h"
using namespace std;
class GbkText:public Text
{
public:
GbkText(string path);
~GbkText(void);
bool ReadOneChar(string & oneChar);
};
#endifView Code
GBKText类的实现如下:
1 #include "GbkText.h"
2 GbkText::GbkText(string path):Text(path){}
3 GbkText::~GbkText(void) {}
4 bool GbkText::ReadOneChar(string & oneChar)
5 {
6 // return true 表示读取成功,
7 // return false 表示已经读取到流末尾
8 if(m_length == m_index)
9 return false;
10 if((unsigned char)m_binaryStr[m_index] < 0x81)
11 {
12 oneChar = m_binaryStr[m_index];
13 m_index++;
14 }
15 else
16 {
17 oneChar = string(m_binaryStr, 2);
18 m_index += 2;
19 }
20 return true;
21 }View Code
UtfText类的定义如下:
1 #ifndef UTFTEXT_H
2 #define UTFTEXT_H
3 #include4 #include5 #include "Text.h"
6 using namespace std;
7 class UtfText:public Text
8 {
9 public:
10 UtfText(string path);
11 ~UtfText(void);
12 bool ReadOneChar(string & oneChar);
13 private:
14 size_t get_utf8_char_len(const char & byte);
15 };
16 #endifView Code
UtfText类的实现如下:
1 #include "UtfText.h"
2 UtfText::UtfText(string path):Text(path){}
3 UtfText::~UtfText(void) {}
4 bool UtfText::ReadOneChar(string & oneChar)
5 {
6 // return true 表示读取成功,
7 // return false 表示已经读取到流末尾
8 if(m_length == m_index)
9 return false;
10 size_t utf8_char_len = get_utf8_char_len(m_binaryStr[m_index]);
11 if( 0 == utf8_char_len )
12 {
13 oneChar = "";
14 m_index++;
15 return true;
16 }
17 size_t next_idx = m_index + utf8_char_len;
18 if( m_length < next_idx )
19 {
20 //cerr << "Get utf8 first byte out of input src string." << endl;
21 next_idx = m_length;
22 }
23 //输出UTF-8的一个字符
24 oneChar = string(m_binaryStr + m_index, next_idx - m_index);
25 //重置偏移量
26 m_index = next_idx;
27 return true;
28 }
29
30
31 size_t UtfText::get_utf8_char_len(const char & byte)
32 {
33 // return 0 表示错误
34 // return 1-6 表示正确值
35 // 不会 return 其他值
36
37 //UTF8 编码格式:
38 // U-00000000 - U-0000007F: 0xxxxxxx
39 // U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
40 // U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
41 // U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
42 // U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
43 // U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
44
45 size_t len = 0;
46 unsigned char mask = 0x80;
47 while( byte & mask )
48 {
49 len++;
50 if( len > 6 )
51 {
52 //cerr << "The mask get len is over 6." << endl;
53 return 0;
54 }
55 mask >>= 1;
56 }
57 if( 0 == len)
58 {
59 return 1;
60 }
61 return len;
62 }View Code
工厂类TextFactory的类定义如下:
1 #ifndef TEXTFACTORY_H
2 #define TEXTFACTORY_H
3 #include4 #include "Text.h"
5 #include "UtfText.h"
6 #include "GbkText.h"
7 using namespace std;
8 class TextFactory
9 {
10 public:
11 static Text * CreateText(string textCode, string path);
12 };
13 #endifView Code
工厂类的实现如下:
1 #include "TextFactory.h"
2 #include "Text.h"
3 Text * TextFactory::CreateText(string textCode, string path)
4 {
5 if( (textCode == "utf-8")
6 || (textCode == "UTF-8")
7 || (textCode == "ISO-8859-2")
8 || (textCode == "ascii")
9 || (textCode == "ASCII")
10 || (textCode == "TIS-620")
11 || (textCode == "ISO-8859-5")
12 || (textCode == "ISO-8859-7") )
13 {
14 return new UtfText(path);
15 }
16 else if((textCode == "windows-1252")
17 || (textCode == "Big5")
18 || (textCode == "EUC-KR")
19 || (textCode == "GB2312")
20 || (textCode == "ISO-2022-CN")
21 || (textCode == "HZ-GB-2312")
22 || (textCode == "gb18030"))
23 {
24 return new GbkText(path);
25 }
26 return NULL;
27 }View Code
测试的Main函数如下:
1 #include2 #include3 #include4 #include "Text.h"
5 #include "TextFactory.h"
6 #include "CodeDetector.h"
7 using namespace std;
8 int main(int argc, char *argv[])
9 {
10 string path ="日文";
11 string code ="utf-8";
12 Text * t = TextFactory::CreateText(code, path);
13 string s;
14 while(t->ReadOneChar(s))
15 {
16 cout<<s;
17 }
18 delete t;
19 }View Code
编译运行后即可在控制台输出正确的文本。





