• 使用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
    View 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."<<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 }
    View Code

    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
    View 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 #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
    View 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 #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
    View 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 #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 }
    View Code

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

     

     
  • 相关阅读:
    三层架构(我了解并详细分析)
    define a class for a linked list and write a method to delete the nth node.
    无阻塞情况connect生产EINPROGRESS错
    辛星和你解读PHP递归
    Android -- Looper.prepare()和Looper.loop() —深度版
    Canvas翻转方法
    BP神经网络的基本原理
    muduo网络图书馆评测
    HBASE
    MySQL
  • 原文地址:https://www.cnblogs.com/jostree/p/4374404.html
Copyright © 2020-2023  润新知