C++ 读取 UTF-8 及 GBK 系列的文本方法及原理

举报
C语言与CPP编程 发表于 2021/11/20 23:34:09 2021/11/20
【摘要】 1.读取 UTF-8 编码文本原理 首先了解 UTF-8 的编码方式,UTF-8 采用可变长编码的方式,一个字符可占 1 字节 -6 字节,其中每个字符所占的字节数由字符开始的 1 的个数确定,具体的编码方式如下: U-00000000 – U-0000007F: 0xxxxxxxU-00000080 – U-000007FF...

1.读取 UTF-8 编码文本原理

首先了解 UTF-8 的编码方式,UTF-8 采用可变长编码的方式,一个字符可占 1 字节 -6 字节,其中每个字符所占的字节数由字符开始的 1 的个数确定,具体的编码方式如下:


   
  1. U-00000000 – U-0000007F: 0xxxxxxx
  2. U-00000080 – U-000007FF: 110xxxxx 10xxxxxx
  3. U-00000800 – U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
  4. U-00010000 – U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
  5. U-00200000 – U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  6. 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 抽象类的定义如下:


   
  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. void Text::SetIndex(size_t index)
  27. {
  28. m_index = index;
  29. }
  30. size_t Text::Size()
  31. {
  32. return m_length;
  33. }
  34. Text::~Text()
  35. {
  36. delete [] m_binaryStr;
  37. }

GBKText 类的定义如下:


   
  1. #ifndef GBKTEXT_H
  2. #define GBKTEXT_H
  3. #include <iostream>
  4. #include <string>
  5. #include "Text.h"
  6. using namespace std;
  7. class GbkText:public Text
  8. {
  9. public:
  10. GbkText(string path);
  11. ~GbkText(void);
  12. bool ReadOneChar(string & oneChar);
  13. };
  14. #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. size_t UtfText::get_utf8_char_len(const char & byte)
  30. {
  31. // return 0 表示错误
  32. // return 1-6 表示正确值
  33. // 不会 return 其他值
  34. //UTF8 编码格式:
  35. // U-00000000 - U-0000007F: 0xxxxxxx
  36. // U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
  37. // U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
  38. // U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
  39. // U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  40. // U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  41. size_t len = 0;
  42. unsigned char mask = 0x80;
  43. while( byte & mask )
  44. {
  45. len++;
  46. if( len > 6 )
  47. {
  48. //cerr << "The mask get len is over 6." << endl;
  49. return 0;
  50. }
  51. mask >>= 1;
  52. }
  53. if( 0 == len)
  54. {
  55. return 1;
  56. }
  57. return len;
  58. }

工厂类 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. }

- EOF -

文章来源: blog.csdn.net,作者:C语言与CPP编程,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/weixin_41055260/article/details/121434010

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。