作者: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抽象类的定义如下:

 #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) = ;
size_t Size();
virtual ~Text();
};
#endif

Text抽象类的实现如下:

 #include "Text.h"
using namespace std;
Text::Text(string path):m_index()
{
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(,ios::end,ios::in);
pbuf->pubseekpos(,ios::in);
// 分配内存空间
m_binaryStr = new char[m_length+];
// 获取文件内容
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, );
m_index += ;
}
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( == 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 = ;
unsigned char mask = 0x80;
while( byte & mask )
{
len++;
if( len > )
{
//cerr << "The mask get len is over 6." << endl;
return ;
}
mask >>= ;
}
if( == len)
{
return ;
}
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;
}

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

 

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

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

    由于一些网站通信编码的问题不得不把一直使用的网站编码由UTF8转为GBK,在转换过程中在官方看了很多方法,自己也都尝试了一些最后都没有能够成功,数据库的转换一直都是没有大问题,不存在丢失什么的,能看到 ...

  2. UTF-8和GBK等中文字符编码格式介绍及相互转换

    我们有很多时候需要使用中文编码格式,比如gbk.gb2312等,但是因为主要针对中文编码设置,因此并不完全通用,这样一来就有了在各编码间相互转换的需求,比如和UTF8的转换.可是在我使用的过程中,却发 ...

  3. UNICODE与UTF8和GBK之间的关系

    http://wenku.baidu.com/link?url=bheGEzfSjEx-QX-ciME5oKooKYE08_NJZ02l2kKFa7kVZJ4t8Ks2uSNByovgP2QL6btq ...

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

    python代码文件的编码 py文件默认是ASCII编码,中文在显示时会做一个ASCII到系统默认编码的转换,这时就会出错:SyntaxError: Non-ASCII character.需要在代码 ...

  5. Utf-8 转 GBK

    QTextCodec *gbk = QTextCodec::codecForName("gb18030"); QTextCodec *utf8 = QTextCodec::code ...

  6. python 读取utf8文件

    有时候默认是gbk编码,但是要读取utf8文件,所以会出现decode 错误. 使用codecs模块: import codecs file = codecs.open('filename','r', ...

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

    在使用Visual Studio 2005进行MFC开发的时候,发现自动添加的注释变成了乱码.像这样: // TODO: ÔÚ´ËÌí¼ÓרÓôúÂëºÍ/»òµ÷ÓûùÀà 还有这样: // ...

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

    第一节:UTF-8和GBK编码概述 UTF-8 (8-bit Unicode Transformation Format) 是一种针对Unicode的可变长度字符编码,又称万国码,它包含全世界所有国家 ...

  9. Python读取UTF-8编码文件并使用命令行执行时输出结果的问题

    最近参加了由CCF举办的数据挖掘比赛,主办方提供了csv格式的数据文件,由于中文显示乱码的问题,我先用txt文本编辑器将编码改为utf-8格式,但是在读取文件并输出读取结果时发生了问题,代码如下: # ...

随机推荐

  1. 在Shell中使用函数文件

    需要编写一个较庞大的脚本时,可能会涉及许多函数.变量.这是通常建议将众多的函数.变量放入一个单独的脚本内.这样做的好处很明显,不用担心某个函数.变量是否已经被定义和使用,也不用频繁地定义.清除函数和变 ...

  2. offer档次排名,2014最新版

    转自:http://tieba.baidu.com/p/2748469183 综合考虑发展,薪水,环境,压力. 第0档:美国互联网总部special offer(15万刀起薪) 第一档: 股份制银行总 ...

  3. Java SimpleDateFormat 函数

    一.SimpleDateFormat函数例子: SimpleDateFormat format=new SimpleDateFormat("MM-dd HH:mm:ss E"); ...

  4. Asp.Net 之 母版页中对控件ID的处理

    一.问题提出 由于总体排版和设计的需要,我们往往创建母版页来实现整个网站的统一性,最近我由于统一性的需要,把原来整个项目单独的页面全部套用了母版页.但是出现了一系列失效错误. 二. 抽象模型 由于整个 ...

  5. 简要地写出一个.NET Remoting的示例

    在VS 2008中添加新的类库项目,并命名为NetRmClass,将所属解决方案命名为NetRm,勾选“创建解决方案的目录”.这样,NetRmClass类库项目目录即属于NetRm解决方案,并可以继续 ...

  6. Timed Code

    <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat=&qu ...

  7. asp.net EF model中的默认值设置

    在做数据库规划时,通常会规划一些系统字段,也就是由数据库本身自行指定默认值到这个字段上,创建新的“创建时间(CreateDate)”字段就会常常这样设计. 如果希望能有默认值,且让.net 程序在新增 ...

  8. Person

    using System;using System.Collections.Generic;using System.Linq;using System.Text; namespace PersonD ...

  9. VS2012添加ADO实体数据模型

    最近更新了开发工具为VS2012,在使用EntityFramework创建实体数据模型的时候,在添加选项中找不到这个选项. 确定了自己已经安装了EntityFramework,猜到估计是版本的问题,于 ...

  10. 用Drawing画图如何不会消失

    方法一:将事件放在form_Load中,在窗体中画图   1: protected void MainForm_Load(object sender,EventArgs e) 2: { 3: Init ...