发布时间:2015-03-28 00:00 来源:jostree
首先了解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的个数,就可以确定这个字符的长度。
对于ASCII、GB2312、GBK到GB18030编码方法是向下兼容的 ,即同一个字符在这些方案中总是有相同的编码,后面的标准支持更多的字符。
在这些编码中,英文和中文可以统一地处理。区分中文编码的方法是高字节的最高位不为0。
因此我们只需处理好GB18130,就可以处理与他兼容的所有编码,对于GB18130使用双字节变长编码。
单字节部分从 0×0~0x7F 与 ASCII 编码兼容。双字节部分,首字节从 0×81~0xFE,尾字节从 0×40~0x7E以及 0×80~0xFE,与GBK标准基本兼容。
因此只需检测首字节是否小于0×81即可确定其为单字节编码还是双字节编码。
对于一个语言处理系统,读取不同编码的文本应该是最基础的需求,文本的编码方式应该对系统其他调用者透明,只需每次获取一个字符即可,而不需要关注这个文本的编码方式。从而我们定义了抽象类Text,及其接口ReadOneChar,并使两个文本类GbkText和UtfText继承这个抽象类,当系统需要读取更多种编码的文件时,只需要定义新的类然后继承该抽象类即可,并不需要更改调用该类的代码。从而获得更好的扩展性。
更好的方式是使用简单工厂模式,使不同的文本编码格式对于调用类完全透明,简单工厂模式详解请参看:C++实现设计模式之 — 简单工厂模式
其中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; }