字符编码及其在 C++ 与 Python 中的使用

作者:CPPFive

在网上爬句子的时候碰到了编码转换的问题。因为很多面向世界范围网页上普遍使用 UTF-8 编码,而本地默认使用的是 GBK(准确来说是CP936,很类似 GBK 但不完全一样)编码,并且将 UTF-8 编码直接转换成 GBK 编码有概率会爆出乱码(UTF-8 编码对应的字符集大于 GBK),所以我们需要一种能在本地处理 UTF-8 的方法。这里主要介绍一些关于计算机编码的基本知识以及在 C++ 和 Python 中处理不同编码的文件的方式。

编码

字符是以二进制形式存储在电脑中的。而为了使电脑中存储的二进制编码可以在不同的电脑上被使用,就需要通用的编码方式,即有一套类似于密码表一样的东西,使得每一个字符都可以与一个二进制数(这个二进制数通常被称为码位)相对应。ASCII 码就是非常基础的一套“密码表”,可以将常见符号以及英文字母对应成二进制数。但是ASCII码的每个字符只使用八个二进制位来存储,所以最多也只能对应 256 个字符(实际上标准 ASCII 码只使用七个二进制位,只包含 128 个字符)。而为了存储更多语言的字符,出现了更大的字符集。

Unicode

统一码(Unicode),也叫万国码、单一码,是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。

以上是度娘上的定义。Unicode 指的并不是某一个字符集或者编码方式,而是一项包含了若干字符集以及编码方式的编码标准。我们知道 ASCII 码的每一个字符用一个八位二进制数存储,而 Unicode 为了容纳更多字符,使用了更多字节。Unicode 中常用的字符集有 UCS-2(Universal Character Set coded in 2 octets)以及 UCS-4 ,前者使用两个字节,后者使用四个字节,这些字符集中每一个字符对应的二进制数被称为它的码位(code point)。UCS-2 共可以容纳 65536 个字符,而后来为了容纳更多文字,才发展出了 UCS-4,后者共可以容纳 1114112 个字符,人类文明所创造出的字符总量目前还远未达到这个数字。

要注意的是,UCS-2 和 UCS-4 仅仅规定了文字和它的码位之间的对应关系,并没有规定这些码位在计算机中如何存储。事实上,由于常用字符并不需要这么大的空间来存储,因此可以通过一些编码方式来节省空间,即通过某种方式将较长的码位一一对应为较短的实际编码。这些编码方式被称为 UTF(Unicode Transformation Format),其中应用较多的是 UTF-16 和 UTF-8。通常字符通过这些编码方式存储在计算机中。

GB2312、GBK、GB18030

三者都是国内常用的编码标准,其中 GB2312 和 GB18030 属于国家标准,GBK 则是二者中承上启下的部分。由于某些历史原因,GBK 至今仍然在国内被大量使用。

与 Unicode 字符相比,这几种编码标准有如下的几点不同:

  1. 编码不同(废话),比如“国”这个字,在 UTF-8 中编码为 E59BBD(编码通常用十六进制数表示),在 GBK 中编码则为 B9FA。文末附了几个常用的查看字符编码的方式,有兴趣的读者可以去实践一下。
  2. Unicode 中每个字符的码位与计算机中实际存储的信息是不同的(因为采用了 UTF-8 等编码方式),而 GB2312、GBK、GB18030 的字符集则是与它的存储方式一致的,或者说此三者既是字符集,也是编码方式。

GB2312 只收录了简体汉字和一些常用符号和字母,共收录了 6763 个汉字;GBK 在 GB2312 的基础上扩充了一部分新增的汉字以及繁体字、日语、朝鲜语中的汉字,共收录了两万多个汉字和字符;GB18030 在 GBK 的基础上进一步扩充,收录了七万多个汉字和字符。GB18030 字符集实际上非常大,也与 Unicode 一样包含了世界上大部分字符。

之所以在已经有了 Unicode 的基础上我国还要自己开发 GB18030,原因是 GB18030 是完全面向我国的,它的编码方式更利于使用中文,例如在 GB18030 中存储大部分中文字符都只需要两个字节,而 使用 UTF-8 则需要三个字节。当然实际上由于许多主流应用软件与系统软件都是由外国开发的,不太支持 GB18030 (尤其是 Windows),因此我们平常接触更多的还是 UTF-8 或者 GBK。

不同编码的使用

Unicode 的优点是它足以容纳所有语言的字符,因此可以满足在各国之间通用的要求(想象一下假如不同国家之间使用的是各自的编码,那么每一次传输信息都将需要进行编码的转换,这是相当繁琐的)。但是通常使用中远远用不到那么多那么多字符,而 Unicode 中每个字符所占据的较大的空间此时就成为了负担。所以 GBK 这一类的只包含部分语言、占用空间也较少的字符集在各国国内仍然在大量使用。这也导致了我们前面提到的问题。

有些时候我们在访问国外网站的时候会出现乱码也是因为这种原因。网站的代码可能使用了 UTF-8 编码,但是没有在文档中进行声明,这时如果访问者的浏览器默认使用 GBK 编码,就会出现乱码。

C++中的编码问题

C++中提供了宽字节 wchar_t 以及相应的一些函数(其实就是原来的函数前面加个 w)来处理 Unicode 字符。似乎由于UTF-16 的大部分常用字符都是两个字节,而一个宽字节恰好也是两个字节,所以可以直接处理。代码如下所示:

wstring sss;
wifstream go("test.txt");
go >> sss;
wofstream back("100.txt");
back << sss;

但以上代码无法处理使用 UTF-8 编码的文件,好在 C++ 提供了相应的函数。我们可以使用 locale 函数来调整输入/输出流采用的编码方式,这样就可以处理 UTF-8 编码的文件了。代码如下:

wstring sss;
locale china(".65001");
wifstream go("test.txt");
go.imbue(china);
go >> sss;
wofstream back("100.txt");
back.imbue(china);
back << sss;

上面的 china 是locale 类中的一个实例,括号里的 .65001 是系统中 UTF-8 的编号。系统中编码方式被称为”活动代码页“,不同的活动代码页有不同的编号,文末附了一份常用编码方式对应代码页编号的列表。

另外,需要注意的是,如果是要输出到命令行里的话,那么输出流是会受限于命令行的编码方式的。如果一定要输出到命令行中,可以参考这篇文章来修改命令行使用的编码方式。

非常有意思的是,如果用 CP936(即 GBK)来输入输出 UTF-8 的文件,那么在有些时候不会出现乱码,另一些时候则会出现。

源文件

目标文件中可以正常显示

源文件

目标文件出现乱码

经过不完全测试,在输出大部分汉字以及纯 ASCII 字符的时候都不会出现问题,但是当文件中包含字符”四“时就会出现乱码(应该是一部分汉字会导致问题),并且在 ASCII 字符和中文混合使用的时候也必定会出现乱码。

Python中的字符编码问题

Python 非常的强大,这一点是毫无疑问的。在字符编码的问题上,Python 明显比 C++ 方便得多。Python3 中的字符串(即str)统一以 Unicode 字符的形式保存。(Unicode 并不是一种编码方式。之所以说 Python3 使用 Unicode 字符保存字符串,是因为在网上查不到其具体的编码方式,只有这种笼统的介绍;经过本人测试,Python3 中一个英文字符占一个字节,一个常用中文字符占两个字节,所以应该可以排除单独使用的 UTF-8、UTF-16 或者 UTF-32 的可能;比较有可能是多种编码方式混合使用)。而在读入和输出时,Python3 会自动根据文件的类型来完成转换。以下面的代码为例:

with open("test.txt","r",encoding='utf-16') as f,open("100.txt","w",encoding='utf-8') as p:
    sss=f.readline()
    p.write(sss)
    f.close()
    p.close()

上面的程序从一个 UTF-16 编码的文件读取内容,存储到一个字符串中,再以 UTF-8 编码输出到文件中。只需要指定文件的编码方式,Python3 就会为你做好一切。如果不指定的话,Python3 会默认使用系统的编码方式。

正文就到这里结束啦,欢迎大家点赞评论支持一下(~ ̄▽ ̄)~

查看字符编码的方式

1.在线转换

点这里

2.使用 Python 查看

dest = "中"      # 待查看编码的字符/字符串 
dest_encode = dest.encode("utf-8")      # utf-8 可以换成别的编码方式
print(dest_encode)

3.使用 Word

在 Word 里可以通过 alt+X 来将光标前的汉字/UTF-16 编码互相转换。暂时不太清楚是否可以转换成别的编码。

常用编码方式对应代码页编号的列表

代码页       国家(地区)或语言 
437          美国 
708          阿拉伯文(ASMO 708)
720          阿拉伯文(DOS)
850          多语言(拉丁文 I) 
852          中欧(DOS) - 斯拉夫语(拉丁文 II) 
855          西里尔文(俄语) 
857          土耳其语 
860          葡萄牙语 
861          冰岛语 
862          希伯来文(DOS)
863          加拿大 - 法语 
865          日耳曼语 
866          俄语 - 西里尔文(DOS) 
869          现代希腊语
874          泰文(Windows)
932          日文(Shift-JIS)
936          中国 - 简体中文(GB2312)
949          韩文
950          繁体中文(Big5)
1200         Unicode        
1201         Unicode (Big-Endian)
1250         中欧(Windows)
1251         西里尔文(Windows)
1252         西欧(Windows)
1253         希腊文(Windows)
1254         土耳其文(Windows)
1255         希伯来文(Windows)
1256         阿拉伯文(Windows)
1257         波罗的海文(Windows)
1258         越南文(Windows)
20866        西里尔文(KOI8-R)
21866        西里尔文(KOI8-U)
28592        中欧(ISO)
28593        拉丁文 3 (ISO)
28594        波罗的海文(ISO)
28595        西里尔文(ISO)
28596        阿拉伯文(ISO)
28597        希腊文(ISO)
28598        希伯来文(ISO-Visual)
38598        希伯来文(ISO-Logical)
50000        用户定义的
50001        自动选择
50220        日文(JIS)
50221        日文(JIS-允许一个字节的片假名)
50222        日文(JIS-允许一个字节的片假名 - SO/SI)
50225        韩文(ISO)
50932        日文(自动选择)
50949        韩文(自动选择)
51932        日文(EUC)
51949        韩文(EUC)
52936        简体中文(HZ)
65000        Unicode (UTF-7)
65001        Unicode (UTF-8)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,753评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,668评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,090评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,010评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,054评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,806评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,484评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,380评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,873评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,021评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,158评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,838评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,499评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,044评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,159评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,449评论 3 374
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,136评论 2 356