弄清楚编码
Contents
由于工作环境经常在各种平台,各种语言和区域环境之间切换,乱码一直是一个讨厌的问题,乱码问题大多源于编码。所以想再这里把编码问题弄清楚,也分享一下学习心得。
从计算机对多国语言的支持角度看,大致可以分为三个阶段:
- ASCII
- ANSI
- UNICODE
编码
-
ASCII编码 ASCII码一共规定了128个字符的编码,比如空格"SPACE"是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的1位统一规定为0。 英语用128个符号编码就够了,但是用来表示其他语言,128个符号是不够的。一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号。但是不同的国家有不同的字母,编码字符集是不一样的,比如,130在法语编码中代表了é,在希伯来语编码中却代表了字母Gimel (ג)。
-
ANSI编码 为使计算机支持更多语言,使用2个字节来表示1个字符。这样不同的国家和地区制定了不同的标准,由此产生了
GB2312
,BIG5
,JIS
等各自的编码标准。这些使用 2 个字节来代表一个字符的各种汉字延伸编码方式,称为ANSI
编码。在简体中文系统下,ANSI
编码代表GB2312
编码,在日文操作系统下,ANSI
编码代表JIS
编码。 -
UNICODE 不同
ANSI
编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段ANSI
编码的文本中。为了使国际间信息交流更加方便,国际组织制定了UNICODE
字符集,为各种语言中的每一个字符设定了统一并且唯一的数字编号,以满足跨语言、跨平台进行文本转换、处理的要求。Unicode当然是一个很大的集合,现在的规模可以容纳100多万个符号,每个符号的编码都不一样。
编码与存储
说到编码,很容易混淆Unicode
和UTF-8
,我就很长时间没有分清楚。事实上,Unicode
只是一个符号集,它只规定了符号的二进制编码,却没有规定这个二进制编码应该如何存储。而UTF-8
是最常用的一种Unicode
的存储方式,其他实现方式还包括UTF-16
(字符用两个字节或四个字节表示)和UTF-32
(字符用四个字节表示)。UTF-8
编码是一种变长编码,可以使用1~4个字节表示一个符号。
UTF-8
编码的存储规则很简单,只有二条:
- 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的
Unicode
码。对于ASCII
字符,UTF-8
编码和ASCII
码是相同的。 - 对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
Unicode符号范围 | UTF-8编码方式(十六进制) | (二进制) |
---|---|---|
0000 0000-0000 | 007F | 0xxxxxxx |
0000 0080-0000 | 07FF | 110xxxxx 10xxxxxx |
0000 0800-0000 | FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
0001 0000-0010 | FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
UTF-8
编码非常简单,如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。
文件编码
Windows里文本保存编码一般有四个选项:ANSI,Unicode,Unicode big endian 和 UTF-8。
- ANSI是默认的编码方式。对于英文文件是ASCII编码,对于简体中文文件是gb2312编码(只针对Windows简体中文版,如果是繁体中文版会采用Big5码)。
- Unicode编码指的是UCS-2编码方式,即直接用两个字节存入字符的Unicode码。这个选项用的little endian格式。
- Unicode big endian编码与上一个选项相对应。
- UTF-8编码,也就是上一节谈到的编码方法。
Unicode规范中定义,每一个文件的最前面分别加入一个表示编码顺序的字符,这个字符的名字叫做"零宽度非换行空格”(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。这正好是两个字节,而且FF比FE大1。如果一个文本文件的头两个字节是FE FF,就表示该文件采用大头方式;如果头两个字节是FF FE,就表示该文件采用小头方式。
跨平台工作会经常遇到文件编码转换的问题,Windows中默认的文件格式是GBK(gb2312),而Linux一般都是UTF-8。
- 查看文件编码
-
在Vim 中
:set fileencoding
可以直接查看文件编码。 另外在.vimrc中加上set encoding=utf-8 fileencodings=ucs-bom,utf-8,cp936
,可以让vim自动识别文件编码,其实就是依照fileencodings提供的编码列表尝试,如果没有找到合适的编码,就用latin-1(ASCII)编码打开。 -
enca 查看文件编码
|
|
- 文件编码转换
-
在Vim中直接进行转换文件编码
:set fileencoding=utf-8
。 -
enconv 转换文件编码,比如要将一个GBK编码的文件转换成UTF-8编码:
enconv -L zh_CN -x UTF-8 filename
-
iconv 转换:
iconv -f encoding -t encoding inputfile
比如将一个UTF-8 编码的文件转换成GBK编码:
|
|
C/C++字符串常量
在C++代码中,给一个string类型的变量赋值一个中文字符串常量,例如:string s = "中文字符串"
。变量s中保存的字节内容是什么?如果源文件的编码格式转换了,比如从GB2312
转换为UTF-8
,变量s
中的内容会发生变化吗?其结果是否与编译器有关?
VC++会试图将Unicode的编码格式转换成对应地区(Locale)的缺省编码(简体中文系统下,为GB2312即代码页CP936),并按照这个编码的内容来确定常量字符串的值。G++不会试图转换常量的字符串编码,会直接使用与源文件字符编码对应的字符串常量。
在VC++中,如果源程序的编码与当前默认ANSI编码不符,则需要使用#pragma setlocale
告诉编译器源程序使用的编码:
|
|
Author olOwOlo
LastMod 0001-01-01