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 使用双字节变长编码。
单字节部分从 0×0~0x7F 与 ASCII 编码兼容。双字节部分,首字节从 0×81~0xFE,尾字节从 0×40~0x7E以及 0×80~0xFE,与 GBK 标准基本兼容。
因此只需检测首字节是否小于 0×81 即可确定其为单字节编码还是双字节编码。
3.C++ 代码实现
对于一个语言处理系统,读取不同编码的文本应该是最基础的需求,文本的编码方式应该对系统其他调用者透明,只需每次获取一个字符即可,而不需要关注这个文本的编码方式。从而我们定义了抽象类 Text,及其接口ReadOneChar,并使两个文本类 GbkText 和 UtfText 继承这个抽象类,当系统需要读取更多种编码的文件时,只需要定义新的类然后继承该抽象类即可,并不需要更改调用该类的代码。从而获得更好的扩展性。
更好的方式是使用简单工厂模式,使不同的文本编码格式对于调用类完全透明,简单工厂模式详解请参看:设计模式:可复用面向对象软件基础。
其中 Text 抽象类的定义如下:
-
#ifndef TEXT_H
-
#define TEXT_H
-
#include <iostream>
-
#include <fstream>
-
using namespace std;
-
class Text
-
{
-
protected:
-
char * m_binaryStr;
-
size_t m_length;
-
size_t m_index;
-
public:
-
Text(string path);
-
void SetIndex(size_t index);
-
virtual bool ReadOneChar(string &oneChar) = 0;
-
size_t Size();
-
virtual ~Text();
-
};
-
#endif
Text 抽象类的实现如下:
-
#include "Text.h"
-
using namespace std;
-
Text::Text(string path):m_index(0)
-
{
-
filebuf *pbuf;
-
ifstream filestr;
-
// 采用二进制打开
-
filestr.open(path.c_str(), ios::binary);
-
if(!filestr)
-
{
-
cerr<<path<<" Load text error."<<endl;
-
return;
-
}
-
// 获取filestr对应buffer对象的指针
-
pbuf=filestr.rdbuf();
-
// 调用buffer对象方法获取文件大小
-
m_length=(int)pbuf->pubseekoff(0,ios::end,ios::in);
-
pbuf->pubseekpos(0,ios::in);
-
// 分配内存空间
-
m_binaryStr = new char[m_length+1];
-
// 获取文件内容
-
pbuf->sgetn(m_binaryStr,m_length);
-
//关闭文件
-
filestr.close();
-
}
-
-
-
void Text::SetIndex(size_t index)
-
{
-
m_index = index;
-
}
-
-
-
size_t Text::Size()
-
{
-
return m_length;
-
}
-
-
-
Text::~Text()
-
{
-
delete [] m_binaryStr;
-
}
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 类的实现如下:
-
#include "GbkText.h"
-
GbkText::GbkText(string path):Text(path){}
-
GbkText::~GbkText(void) {}
-
bool GbkText::ReadOneChar(string & oneChar)
-
{
-
// return true 表示读取成功,
-
// return false 表示已经读取到流末尾
-
if(m_length == m_index)
-
return false;
-
if((unsigned char)m_binaryStr[m_index] < 0x81)
-
{
-
oneChar = m_binaryStr[m_index];
-
m_index++;
-
}
-
else
-
{
-
oneChar = string(m_binaryStr, 2);
-
m_index += 2;
-
}
-
return true;
-
}
UtfText 类的定义如下:
-
#ifndef UTFTEXT_H
-
#define UTFTEXT_H
-
#include <iostream>
-
#include <string>
-
#include "Text.h"
-
using namespace std;
-
class UtfText:public Text
-
{
-
public:
-
UtfText(string path);
-
~UtfText(void);
-
bool ReadOneChar(string & oneChar);
-
private:
-
size_t get_utf8_char_len(const char & byte);
-
};
-
#endif
UtfText 类的实现如下:
-
#include "UtfText.h"
-
UtfText::UtfText(string path):Text(path){}
-
UtfText::~UtfText(void) {}
-
bool UtfText::ReadOneChar(string & oneChar)
-
{
-
// return true 表示读取成功,
-
// return false 表示已经读取到流末尾
-
if(m_length == m_index)
-
return false;
-
size_t utf8_char_len = get_utf8_char_len(m_binaryStr[m_index]);
-
if( 0 == utf8_char_len )
-
{
-
oneChar = "";
-
m_index++;
-
return true;
-
}
-
size_t next_idx = m_index + utf8_char_len;
-
if( m_length < next_idx )
-
{
-
//cerr << "Get utf8 first byte out of input src string." << endl;
-
next_idx = m_length;
-
}
-
//输出UTF-8的一个字符
-
oneChar = string(m_binaryStr + m_index, next_idx - m_index);
-
//重置偏移量
-
m_index = next_idx;
-
return true;
-
}
-
-
-
size_t UtfText::get_utf8_char_len(const char & byte)
-
{
-
// return 0 表示错误
-
// return 1-6 表示正确值
-
// 不会 return 其他值
-
-
-
//UTF8 编码格式:
-
// 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
-
-
-
size_t len = 0;
-
unsigned char mask = 0x80;
-
while( byte & mask )
-
{
-
len++;
-
if( len > 6 )
-
{
-
//cerr << "The mask get len is over 6." << endl;
-
return 0;
-
}
-
mask >>= 1;
-
}
-
if( 0 == len)
-
{
-
return 1;
-
}
-
return len;
-
}
工厂类 TextFactory 的类定义如下:
-
#ifndef TEXTFACTORY_H
-
#define TEXTFACTORY_H
-
#include <iostream>
-
#include "Text.h"
-
#include "UtfText.h"
-
#include "GbkText.h"
-
using namespace std;
-
class TextFactory
-
{
-
public:
-
static Text * CreateText(string textCode, string path);
-
};
-
#endif
工厂类的实现如下:
-
#include "TextFactory.h"
-
#include "Text.h"
-
Text * TextFactory::CreateText(string textCode, string path)
-
{
-
if( (textCode == "utf-8")
-
|| (textCode == "UTF-8")
-
|| (textCode == "ISO-8859-2")
-
|| (textCode == "ascii")
-
|| (textCode == "ASCII")
-
|| (textCode == "TIS-620")
-
|| (textCode == "ISO-8859-5")
-
|| (textCode == "ISO-8859-7") )
-
{
-
return new UtfText(path);
-
}
-
else if((textCode == "windows-1252")
-
|| (textCode == "Big5")
-
|| (textCode == "EUC-KR")
-
|| (textCode == "GB2312")
-
|| (textCode == "ISO-2022-CN")
-
|| (textCode == "HZ-GB-2312")
-
|| (textCode == "gb18030"))
-
{
-
return new GbkText(path);
-
}
-
return NULL;
-
}
测试的 Main 函数如下:
-
#include <stdio.h>
-
#include <string.h>
-
#include <iostream>
-
#include "Text.h"
-
#include "TextFactory.h"
-
#include "CodeDetector.h"
-
using namespace std;
-
int main(int argc, char *argv[])
-
{
-
string path ="日文";
-
string code ="utf-8";
-
Text * t = TextFactory::CreateText(code, path);
-
string s;
-
while(t->ReadOneChar(s))
-
{
-
cout<<s;
-
}
-
delete t;
-
}
- EOF -
文章来源: blog.csdn.net,作者:C语言与CPP编程,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/weixin_41055260/article/details/121434010
- 点赞
- 收藏
- 关注作者
评论(0)