字符编码
ASCII
ASCII is the American Standard Code for Information Interchange ASCII编码用一个字节来表示一个字符, 总共定义了128个字符的编码(包括大小写的英文字母,特殊符号,控制字符)
Unicode
由于不同的国家使用不同的字符 即使都采用256个符号的编码方式,不同的编码方式会导致相同的数字可能对应不同的符号
有的语言使用的符号远远超过了256个字符,所以就必须采用多个字节来表示一个符号 例如简体中文常见的编码方式是GB2312
如上所述,世界上存在多种编码方式,同一个二进制可能被解释为不同的符号, 因此要查看一个文件,就必须要知道字符的编码方式,否则用错误的编码方式就会出现乱码 并且如果一个文件里面使用了多种语言,那么由于编码方式不同,也会出现乱码
为了解决以上问题,就定义了一种编码,可以将世界上所有的符号都纳入其中, 每个符号都包含一个独一无二的编码, 这就是Unicode 例如:
U+0041表示英语的大写字母 'A' U+4E2D表示汉字 '中'
unicode 只是一个符号集, 只规定了符号的二进制代码,却没有规定这个二进制代码该如何存储 比如汉字 '国'的unicode编码为 0x56fd, 至少需要用2个字节才能存储,表示其他符号可能需要更多的字节
有如下两个问题需要解决: 1. 如何区分unicode和ascii? 2. 计算机怎么知道3个字节表示一个符号,或者是3个字节分别表示3个符号?
如果统一规定每个字符用4个字符表示,那么每个英文字符都有3个字节为0, 这样极大地浪费存储空间
UTF-8
UTF-8 只是 unicode的一种实现方式之一 UTF-8的特点: 采用变长编码,它可以使用1~4个字节表示一个符号
编码规则: 1. 对于单字节的符号,字节的第一位设为0, 后面7位为这个字符的unicode码
因此ASCII可以看作UTF-8的子集
2. 对于大于n字节的符号(n > 1)
第一个字节的前n位都设置为1,第n+1位设为0 后面的字节前两位一律10 其余的位用字符的unicode填充
unicode 符号范围 | UTF-8编码字节数 | UTF-8编码方式 |
—- | —- | —- |
十六进制 | - | 二进制 |
0000 0000 - 0000 007F | 1 | 0xxxxxxx |
0000 0080 - 0000 07FF | 2 | 110xxxxx 10xxxxxx |
0000 0800 - 0000 FFFF | 3 | 1110xxxx 10xxxxxx 10xxxxxx |
0001 0000 - 0010 FFFF | 4 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
0020 0000 - 03FF FFFF | 5 | 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx |
0400 0000 - 7FFF FFFF | 6 | 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx |
转换示例: 例如: unicode 0x4F60, 表示中文 '你' 从上表可知 0x4F60 在UTF-8编码下需要用3个字节编码 3字节的UTF-8编码格式为 1110 xxxx 10xx xxxx 10xx xxxx 0x4F60的二进制表示为 0100 1111 0110 0000 填充到3字节的UTF-8格式中得到:
1110 xxxx 10xx xxxx 10xx xxxx 0100 11 1101 10 0000
1110 0100 1011 1101 1010 0000 即 e4bda0
下面使用iconv 和 xxd 工具来验证:
将unicode转换为UTF-8编码,可用如下命令实现: 例如: unicode 0x4E25
echo "25 4e" | xxd -r -p | iconv -f UNICODE -t UTF-8 | xxd -p 输出结果为: e4b8a5 xxd -r -p的输入字符串为 "25 4e", 低字节在前,高字节在后 输出结果也是第字节在前,高字节在后, 所以 e4为低字节, a5为高字节 echo "604f" | xxd -r -p | iconv -f UNICODE -t UTF-8 | xxd -p 输出结果为: e4bda0
unicode字节序
对于unicode 0x4F60, 有两个字节0x60和0x4F 如果采用big endian方式存储, 则第一个字节为0x4F,第二个字节为0x60 如果采用little endian方式存储, 则第一个字节为0x60,第二个字节为0x4F
unicode规范定义,每个文件的前面加入两个字节表示编码顺序 如果文件前两个字节分别是 fe ff, 则表示big endian 如果文件前两个字节分别是 ff fe, 则表示little endian
例: 新建一个文本文档,文件内容为 '你', 编码格式为UTF-8
$ cat test.txt 你 $ file test.txt test.txt: UTF-8 Unicode text
转换为unicode编码:
$ iconv -f UTF-8 -t UNICODE test.txt > test_unicode.txt $ file test_unicode.txt test_unicode.txt: Little-endian UTF-16 Unicode text, with no line terminators $ xxd -g 1 test_unicode.txt 00000000: ff fe 60 4f 0a 00