使用C++读取UTF8及GBK系列的文本方法及原理

作者: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(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 }

GBKText类的定义如下:

#ifndef GBKTEXT_H
#define GBKTEXT_H
#include <iostream>
#include <string>
#include "Text.h"
using namespace std;
class GbkText:public Text
{
public:
    GbkText(string path);
    ~GbkText(void);
    bool ReadOneChar(string & oneChar);
};
#endif

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 }

UtfText类的定义如下:

 1 #ifndef UTFTEXT_H
 2 #define UTFTEXT_H
 3 #include <iostream>
 4 #include <string>
 5 #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 #endif

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 }

工厂类TextFactory的类定义如下:

 1 #ifndef TEXTFACTORY_H
 2 #define TEXTFACTORY_H
 3 #include <iostream>
 4 #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 #endif

工厂类的实现如下:

 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 }

测试的Main函数如下:

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <iostream>
 4 #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 }

编译运行后即可在控制台输出正确的文本。

 

时间: 2024-10-10 14:59:35

使用C++读取UTF8及GBK系列的文本方法及原理的相关文章

discuz编码转换UTF8与GBK互转完美适合Discuz3.x系列

由于一些网站通信编码的问题不得不把一直使用的网站编码由UTF8转为GBK,在转换过程中在官方看了很多方法,自己也都尝试了一些最后都没有能够成功,数据库的转换一直都是没有大问题,不存在丢失什么的,能看到的问题就是会员设置里边没有任何信息,只有保存.这个问题官方有修复方案在这里就不做过多的说明了.现在主要说的问题就是另一个看不见的数据库问题.造成的问题就是门户首页以及社区首页有DIY数据的地方都卡屏,不显示任何东西,页面卡在第一个有DIY调用模块的地方就不加载了.这个问题也一直排查了很久最后尝试了各

rapidxml读取utf-8 格式xml乱码问题(utf-8格式转GBK)

1.我是用rapidxml文件读取utf-8格式的xml,但是显示乱码.我们需要把读出来的内容转为GBK格式,则能正常显示. char * utf82gbk(char* strutf) { //utf-8转为Unicode int size = MultiByteToWideChar(CP_UTF8, 0, strutf, -1, NULL, 0); WCHAR   *strUnicode = new   WCHAR[size]; MultiByteToWideChar(CP_UTF8, 0,

关于解决乱码问题的一点探索之一(涉及utf-8和GBK)

在使用Visual Studio 2005进行MFC开发的时候,发现自动添加的注释变成了乱码.像这样: // TODO: ?ú′?ìí?óרó?′ú??oí/?òμ÷ó??ùàà 还有这样: // TODO: ?ú′?ìí?ó???¢′|àí3ìDò′ú??oí/?òμ÷ó???è??μ 它们正确的显示应该是 // TODO: 在此添加专用代码和/或调用基类    和 // TODO: 在此添加消息处理程序代码和/或调用默认值 当保存的时候,还出现了这样的对话框: 网上找了各种教程,包括什么设

python 处理中文文件时的编码问题,尤其是utf-8和gbk

python代码文件的编码 py文件默认是ASCII编码,中文在显示时会做一个ASCII到系统默认编码的转换,这时就会出错:SyntaxError: Non-ASCII character.需要在代码文件的第一行或第二行添加编码指示: # coding=utf-8 ##以utf-8编码储存中文字符 print '中文'像上面那样直接输入的字符串是按照代码文件的编码来处理的,如果用unicode编码,有以下2种方式: s1 = u'中文' #u表示用unicode编码方式储存信息 s2 = uni

python2读取utf8文件(中文)

直接上代码咯.(python2.7) #encoding=utf-8 #author: walker #date: 2014-11-18 #function: 示例python2读取utf8文件(含中文等特殊字符) import sys   reload(sys)    sys.setdefaultencoding('utf8')   file = open('data.txt', 'r') print('*******************************') for line in

UTF-8和GBK编码之间的区别(页面编码、数据库编码区别)以及在实际项目中的应用

第一节:UTF-8和GBK编码概述 UTF-8 (8-bit Unicode Transformation Format) 是一种针对Unicode的可变长度字符编码,又称万国码,它包含全世界所有国家需要用到的字符,是国际编码,通用性强,是用以解决国际上字符的一种多字节编码.由Ken Thompson于1992年创建.UTF-8用1到4个字节编码UNICODE字符,它对英文使用8位/8Bit(即1个字节/1Byte),中文使用24位/24Bit(3个字节/3Byte)来编码.用在网页上可以同一页

python 读取utf8文件

有时候默认是gbk编码,但是要读取utf8文件,所以会出现decode 错误. 使用codecs模块: import codecs file = codecs.open('filename','r',encoding='utf-8') 使用这个读取带有汉字的文件,如果是汉字,会整个读取进来,而不是按字节读取.读进来会自动转换成unicode.

ios开发中object-c中UTF-8 和 GBK 的 NSString 相互转化的方法

应用都要遇到一个很头疼的问题:文字编码,汉字的 GBK 和 国际通用的 UTF-8 的互相转化稍一不慎, 就会满屏乱码.下面介绍 UTF-8 和 GBK 的 NSString 相互转化的方法 NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000); char* c_test = "北京"; int nLen = strlen(c_test); NSS

UTF-8到GBK转换

实际就是由宽字节转换为多字节的过程 ------------- 首先MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)szOut, -1, (LPWSTR)wszGBK, len) 转换UTF-8 到表中UNICODE 然后 WideCharToMultiByte(CP_ACP, 0, (LPWSTR)wszGBK, -1, szGBK, len, NULL, NULL); UNICODE转换为标准多字节 这个时候已经是ANSI格式了,其实不是GBK格式,但是一般这