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

时间:2021-11-04 20:29:32

我们有很多时候需要使用中文编码格式,比如gbk、gb2312等,但是因为主要针对中文编码设置,因此并不完全通用,这样一来就有了在各编码间相互转换的需求,比如和UTF8的转换。可是在我使用的过程中,却发现编码转换并没有想象中的简单,或者说可能会出错,即使你使用的系统API。我在使用中,产生一些疑惑,搜索资料也没有完全解决我的问题,因此整理了这篇文章。文章末尾列出了我参考的一些资料或者代码实现等,在此谢过。

本文先各个中文编码进行介绍,只做简单介绍,不涉及详细原理(本文结尾附有链接可参阅),然后实例验证编码转换之间的问题。

各个编码格式介绍

  • GB2312

GB2312是*国家汉字信息交换用编码,全称《信息交换用汉字编码字符集——基本集》,由国家标准总局发布,1981年5月1日实施,通行于大陆。新加坡等地也使用此编码。GB2312收录简化汉字及符号、字母、日文假名等共7445个图形字符,其中汉字占6763个。GB2312 规定“对任意一个图形字符都采用两个字节表示,每个字节均采用七位编码表示”,习惯上称第一个字节为“高字节”,第二个字节为“低字节”。

GB2312的编码范围为2121H-777EH,与ASCII有重叠,通行方法是将GB码两个字节的最高位置1以示区别。

  • GBK

GB2312 仅收汉字 6763 个,这大大少于现有汉字,随着时间推移及汉字文化的不断延伸推广,有些原来很少用的字,现在变成了常用字,例如:*的“镕”字,未收入 GB2312-80,现在报业出刊只得使用(金+容)、(金容)、(左金右容)等来表示,形式不一而同,这使得表示、存储、输入、处理都非常不方便,而且这种表示没有统一标准。为了解决这些问题,以及配合UNICODE的实施,全国信息技术化技术委员会于1995年12月1日《汉字内码扩展规范》。GBK向下与GB2312 完全兼容,向上支持ISO 10646 国际标准,在前者向后者过渡过程中起到的承上启下的作用。GBK亦采用双字节表示,即不论中、英文字符均使用双字节来表示,为了区分中文,将其最高位都设定成1,总体编码范围为8140-FEFE之间,首字节在81-FE 之间,尾字节在40-FE 之间,剔除 XX7F 一条线。
GBK 共收入21886个汉字和图形符号,包括:
    * GB2312中的全部汉字、非汉字符号;
    * BIG5中的全部汉字;
    * 与ISO 10646相应的国家标准GB13000中的其它 CJK
汉字,以上合计20902个汉字;
    * 其它汉字、部首、符号,共计984个;

  • GB18030

GB18030 是最新的汉字编码字符集的国家标准,向下兼容 GBK 和 GB2312 标准。GB18030
编码是一二四字节变长编码。一字节部分从 0x0~0x7F 与 ASCII 编码兼容。二字节部分,首字节从 0x81~0xFE,尾字节从 0x40~0x7E以及
0x80~0xFE,与GBK标准基本兼容。四字节部分,第一字节从 0x81~0xFE,第二字节从
0x30~0x39,第三和第四字节的范围和前两个字节分别相同。四字节部分覆盖了从 0x0080 开始, 除去二字节部分已经覆盖的所有 Unicode
3.1码位。也就是说,GB18030编码在码位空间上做到了与Unicode标准一一对应,这一点与UTF-8编码类似。

  • Unicode

Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。Unicode用数字0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符。Unicode只是一个符号集,
它只规定了符号的二进制代码,
却没有规定这个二进制代码应该如何存储。比如UTF-8、UTF-16、UTF-32都是Unicode编码的实现方式,不过UTF-8是使用最多的实现。

  • UTF-8

UTF-8:Unicode Transformation
Format-8bit,允许含BOM,但通常不含BOM。是用以解决国际上字符的一种多字节编码,是在互联网上使用最广的一种unicode的实现方式。UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度,因此可以节省存储空间。它对英文使用8位(即一个字节),中文使用24为(三个字节)来编码。UTF-8包含全世界所有国家需要用到的字符,是国际编码,通用性强。UTF-8编码的文字可以在各国支持UTF8字符集的浏览器上显示。如果是UTF8编码,则在外国人的英文IE上也能显示中文,他们无需下载IE的中文语言支持包。

UTF-8的编码规则很简单,只有二条:

1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的;
            2)对于n字节的符号,第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的二进制位,表示为这个符号的unicode码;

另外像ASCII只用于英文字符编码,BIG5编码是通行于*、香港地区的一个繁体字编码方案,虽然存在一些瑕疵,但广泛应用于电脑行业,尤其是互联网中,从而成为一种事实上的行业标准。

  • 总结
  1. ASCII用于表示英文字符,是用7位表示的,能表示128个字符;其扩展使用8位表示,表示256个字符;
  2. GB2312简体中文的编码格式,
    只支持6763个常用汉字;
  3. GBK是GB2312基础上扩容后兼容GB2312的标准,包含全部中文字符,支持简体中文及繁体中文;
  4. GBK通用性比UTF8差,不过UTF8占用的数据库比GBK大;
  5. GB2312、GBK到GB18030都属于双字节字符集
    (DBCS);
  6. 从ASCII、GB2312、GBK到GB18030,这些编码方法是向下兼容的,即同一个字符在这些方案中总是有相同的编码,后面的标准支持更多的字符。在这些编码中,英文和中文可以统一地处理。区分中文编码的方法是高字节的最高位不为0;

GBK和UTF-8的相互转换

好吧,这才是正题。

从上面看到,一般来说UTF-8可应用于大多数场景,尤其是互联网上,而中文编码主要使用GBK编码,因此这就有了GBK、GB2312和UTF-8的相互转换需求。但要注意,GBK、GB2312等与UTF8之间都必须通过Unicode编码才能相互转换,也即:
    GBK、GB2312 ---> Unicode ---> UTF-8
    UTF8 ---> Unicode
---> GBK、GB2312

比如拿汉字的“字”来举例,

 “字“的表示 二进制表示 16进制 10进制 字符串表示
GBK 11010111 11010110  D7 D6 55254
Unicode 01011011 01010111 5B 57 23383  
Utf-8 11100101 10101101 10010111 E5 AD 97 15052183

同样,对于词语“中国.北京”,转换成UTF-8表示为:涓浗.鍖椾含。

你要问我怎么转换的?原理什么的就不介绍了,在Windo平台下,通过一些Win API可以将GBK和Utf-8经过Unicode层进行相互转换。Linux下也是这样。另外由于各个编码规则都是确定的,因此各字符也就有了一种对应关系,这就使查表法也成为实现编码转换的一种方式。

GBK和UTF-8编码的检测

上面简单介绍了各种编码以及他们的相互转换,但如果给定一个字符串,能够检测出其编码是什么吗? 比如对于“中国.北京”,检测出其为GBK或GB2312,而对于

”涓浗.鍖椾含“检测出其为UTF-8。那么对于一个给定的检测算法,是否保证肯定能检测到该字符串的真正编码方式吗?或者对于一个网页内容来说,很可能既存在GBK编码内容,也有UTF-8内容,那网页应该怎么显示(好像是检测编码,哪种编码出现的频率多,就使用哪种编码显示,臆测,不懂)?

再举一个例子: “谢谢”,其UTF-8表示是“璋㈣阿”。从我的实现中,不论对于GBK编码的“谢谢”,还是UTF-8的“璋㈣阿”,都检测出是UTF-8编码。而我也不是第一个遇到这种情况的人,比如孟岩他老人家也遇到过检测失败的问题,不过他是在Ruby语言下发现的问题,而我是在C/C++实现中出现的检测失败。

好吧,说到这里我也不知道说的是什么了,总之就是,

  • 各编码规则从应用角度来看,还挺复杂;
  • 编码转换和编码检测有时也不靠谱;

附上我的测试代码,请高手指点我的实现是否有问题,谢谢!


 /*下面两个IsTextUTF8都无法检测出gb2312编码的汉字“谢谢”*/
bool IsTextUTF8(const char *str, int length)
{
#ifndef OS_WINDOWS
typedef unsigned long DWORD;
typedef unsigned char UCHAR;
#endif
int i;
DWORD nBytes = ;//UFT8可用1-6个字节编码,ASCII用一个字节
UCHAR chr;
bool bAllAscii = true; //如果全部都是ASCII, 说明不是UTF-8
for(i = ; i < length; i++)
{
chr = (UCHAR) * (str + i); if( (chr & 0x80) != ) // 判断是否ASCII编码,如果不是,说明有可能是UTF-8,ASCII用7位编码,但用一个字节存,最高位标记为0,o0xxxxxxx
bAllAscii = false; if(nBytes == ) //如果不是ASCII码,应该是多字节符,计算字节数
{
if(chr >= 0x80)
{
if(chr >= 0xFC && chr <= 0xFD)
nBytes = ;
else if(chr >= 0xF8)
nBytes = ;
else if(chr >= 0xF0)
nBytes = ;
else if(chr >= 0xE0)
nBytes = ;
else if(chr >= 0xC0)
nBytes = ;
else
{
return false;
}
nBytes--;
}
}
else //多字节符的非首字节,应为 10xxxxxx
{
if((chr & 0xC0) != 0x80 )
{
return false;
}
nBytes--;
}
}
if( nBytes > ) //违返规则
{
return false;
}
if(bAllAscii) //如果全部都是ASCII, 说明不是UTF-8
{
return false;
} return true;
} void GetText(bool isUtf8, std::vector<std::string>& vecStr)
{
const char* filename = isUtf8 ? "utf8.txt" : "gbk.txt";
std::ifstream ifs(filename);
assert(!ifs.fail()); while(!ifs.eof())
{
std::string str;
ifs >> str;
vecStr.push_back(str);
} ifs.close();
} int main()
{
std::vector<std::string> uVecStr;
std::vector<std::string> gVecStr;
GetText(true,uVecStr);
GetText(false,gVecStr); std::cout<<"--------------utf8--->gbk----------------\n";
for (std::vector<std::string>::iterator itr = uVecStr.begin();itr!=uVecStr.end();++itr)
{
std::cout<<*itr<<"\t"<<ConvertUtf8ToGBK(*itr)<<"\t"<<IsTextUTF8((*itr).c_str(),(*itr).size())<<"\t"<<IsTextUTF8(*itr)<<"\n";
} std::cout<<"--------------gbk--->utf8----------------\n";
for (std::vector<std::string>::iterator itr = gVecStr.begin();itr!=gVecStr.end();++itr)
{
std::cout<<*itr<<"\t"<<ConvertGBKToUtf8(*itr)<<"\t"<<IsTextUTF8((*itr).c_str(),(*itr).size())<<"\t"<<IsTextUTF8(*itr)<<"\n";
}
return ;
}
      上面测试代码是分别从两个文本文件中读取相同内容,一个文件内容是UTF-8字符串,然后转换为GBK编码,并检测源字符串编码方式;而另一个文件内容是GBK编码,然后转换为UTF-8编码,并检测源字符串编码方式。文件内容如下:
谢谢
你好
中国.北京

输出内容如下:

--------------utf8--->gbk----------------
锘胯阿璋 ?谢谢
浣犲ソ 你好
涓浗.鍖椾含 中国.北京
--------------gbk--->utf8----------------
谢谢 璋㈣阿
你好 浣犲ソ
中国.北京 涓浗.鍖椾含

本文所用所有代码下载:GBKtoUTF8_cpp.zip且也已共享在github:lizhenghn123

本文所参考文章:

http://blog.csdn.net/liujinchengjx/article/details/1527909
http://www.cnblogs.com/zhenjing/archive/2011/08/07/chinese_string.html
http://zh.wikipedia.org/wiki/GB_2312
http://zh.wikipedia.org/wiki/GBK
http://zh.wikipedia.org/wiki/GB_18030
http://zh.wikipedia.org/wiki/Unicode

http://blog.csdn.net/myan/article/details/1474112(UTF-8编码检测失败)

UTF-8和GBK等中文字符编码格式介绍及相互转换的更多相关文章

  1. ASCII、Unicode和UTF-8等常见字符编码格式介绍

    信息存储在计算机中是转换成二进制来存储的,二进制的发明据说是来源于中国阴阳八卦.后德国数理哲学大师莱布尼茨是最早接触中华文化的欧洲人之一,从他的传教士朋友鲍威特寄给他的拉丁文译本<易经>中 ...

  2. 字符集、字符编码、国际化、本地化简要总结&lpar;UNICODE&sol;UTF&sol;ASCII&sol;GB2312&sol;GBK&sol;GB18030&rpar;

    PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 环境说明   普通的linux 和 普通的windows.    ...

  3. PHP中文字符gbk编码与UTF-8编码的转换

    通常PHP中上传文件,如果文件名称有中文字符,上传之后的名称是无法写入到本地的,因为上传来的编码格式一般是UTF-8的格式,这种格式是无法给文件命名并且存储到操作系统磁盘.在写入之前需要将其转换为gb ...

  4. python中文字符乱码(GB2312,GBK,GB18030相关的问题)

    转自博主 crifan http://againinput4.blog.163.com/blog/static/1727994912011111011432810/ 在玩wordpress的一个博客搬 ...

  5. web工程中文字符乱码:try &lbrace; &Tab;res&period;setContentType&lpar;&quot&semi;text&sol;html&semi;charset&equals;gbk&quot&semi;&rpar;&semi; PrintWriter pw&equals;res&period;getWriter&lpar;&rpar;&semi; &semi;&semi;&semi; &rcub;

    输入正确的name ,pwd  跳转到main 页面 证明:登录信息确认基本正确 用户名,密码不对时提示:信息错误 注意编码格式: 应设置如下:在try中设置字符编码为gbk,在try外有时出错,设置 ...

  6. 【已解决】python中文字符乱码(GB2312,GBK,GB18030相关的问题)

      http://againinput4.blog.163.com/blog/static/1727994912011111011432810/ [已解决]python中文字符乱码(GB2312,GB ...

  7. python 处理html文本的中文字符gbk转utf-8

    #中文字符gbk转utf-8 def gbk2utf8(self,raw): rs=raw.encode('raw_unicode_escape') #转为机器识别字符串 s=repr(rs) ss= ...

  8. ASCII,Unicode,GBK和UTF-8字符编码的区别和联系

    如果经常写python2,肯定会遇到各种“奇怪”的字符编码问题,每次都通过谷歌解决了,但是为什么会造成这种乱码.decode/encode失败等等,本文就字符和字符编码做一个总结,更加清晰区分诸多的编 ...

  9. ASCII、Unicode、GBK和UTF-8字符编码的区别联系

    转自http://dengo.org/archives/901 很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物.他们看到8个开关状态是好的,于是他们把这称 ...

随机推荐

  1. C&num; ASP&period;NET Webservice调用外部exe无效的解决方法

    最近用asp.net做webservice,其中有个功能是调用执行外部的exe(类似cmd中执行),但执行Process.Start之后就没有结果,同样代码在winform下正常,折腾两天终于找到解决 ...

  2. sql 统计用的sql

    mh:工时   mhtype:工时类型(6种) 字段:userid      mhtype    mh       001          1        5       001          ...

  3. nl命令很好,很强大

    指令名称:     nl - 显示文件的行数及内容 语法:    nl [OPTION]... [FILE]... 说明:    将指定文件的内容附加上行数,显示到标准输出. 当没有指定文件名或使用 ...

  4. 第10章 协议和声明类型常量 - IdentityModel 中文文档&lpar;v1&period;0&period;0&rpar;

    使用OAuth 2.0,OpenID Connect和声明时,声明类型和protocoal值有很多"魔术字符串".IdentityModel提供了几个常量字符串类来帮助它. 10. ...

  5. django - 总结 - 中间件

    中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出.因为改变的是全局,所以需要谨慎实用,用不好会影响到性能. MID ...

  6. elasticSearch6源码分析&lpar;6&rpar;http和transport模块

    1.http模块概述 The http module allows to expose Elasticsearch APIs over HTTP. The http mechanism is comp ...

  7. ThreadLocal剧集(一)

    总述     最近做了一个日志调用链路跟踪的项目,涉及到操作标识在线程和子线程,线程池以及远程调用之间的传递问题.最终采用了阿里开源的TransmittableThreadLocal插件(https: ...

  8. LeetCode题解之Number of 1 Bits

    1.题目描述 2.问题分析 使用C++ 标准库的 bitset 类,将整数转换为 二进制,然后将二进制表示转换为字符串,统计字符串中 1 的个数即可. 3.代码 int hammingWeight(u ...

  9. mysql使用常见问题

    常见问题之一: 启动mysql时显示: The server quit without updating PID file 1.可能是/usr/local/mysql/data/mysql.pid文件 ...

  10. Codeforces Round &num;556 &lpar;Div&period; 2&rpar; - C&period; Prefix Sum Primes(思维)

    Problem  Codeforces Round #556 (Div. 2) - D. Three Religions Time Limit: 1000 mSec Problem Descripti ...