一、简介
可能很多人都有遇到过这样一个问题:“字符与字节有什么区别?”
其实,字符与字节它们完全是两个不同的的概念,所以两者之间也没有“区别”这一说法。通俗的来说,字节是一种储存数据的单位,而字符则是一些平常我们使用的文字符号(如字母、数字、标点符号)。
二、字节
字节(Byte)是计算机信息技术用于计量存储容量的一种计量单位,通常情况下一字节等于有八位,也就是二进制的00000000
到11111111
,所以一个字节(Byte)可区别256(2的8次方)
个数字(字符)。
单位换算规则:
8bit(位) = 1Byte
1024Byte = 1KB
1024KB = 1MB
1024MB = 1GB
1024GB = 1TB
1024TB = 1PB
1024PB = 1EB
1024EB = 1ZB
1024ZB = 1YB
1024YB = 1BB
1024BB = 1NB
1024NB = 1DB
三、字符编码
这里先对几个概念做个简单的介绍:
字符——文字与符号的统称(如字母、数字、标点符号)
字符集——就是各种字符的集合
字符编码——就是为字符集中的每一个字符规定一串对应二进制数的一套规则(如在ASCII码中,大写的字母A是01000001
)
字符编码(也称字集码)的种类有非常多,每个国家也会根据自己的文字类型来规定适用的字符编码。
常见的字符编码有:ASCII、 Unicode、UTF-8、GBK(简、繁体字融于一库)、GB2312(简体中文字符集)、Big5(繁体中文字符集)等。
1、ASCII 码
美国根据自己的26个字母,规定了一套共128个字符的编码,叫ASCII码。每个ASCII字符占用1 个字节,而一个字节能够表示256个字符,所以标准的ASCII码只使用了一个字节的后面7位,最前面的1位统一规定为0。
标准ASCII码包含了33个控制字符(具有某些特殊功能但是无法显示的字符)和95个可显示字符。
英语用128个符号编码就够了,但是用来表示其他语言,128个符号很多时候是不够的。所以有些国家就把ASCII码字节中闲置的最高位也编入新的符号,这样就可以表示最多256个符号了。我们把利用128--255这一段的叫做扩展ASCII码。
在 linux 系统中,也可以用如下命令查看ASCII表:
[nosee@instance-4 ~]$ ascii
Usage: ascii [-adxohv] [-t] [char-alias...]
-t = one-line output -a = vertical format
-d = Decimal table -o = octal table -x = hex table -b binary table
-h = This help screen -v = version information
Prints all aliases of an ASCII character. Args may be chars, C \-escapes,
English names, ^-escapes, ASCII mnemonics, or numerics in decimal/octal/hex.
Dec Hex Dec Hex Dec Hex Dec Hex Dec Hex Dec Hex Dec Hex Dec Hex
0 00 NUL 16 10 DLE 32 20 48 30 0 64 40 @ 80 50 P 96 60 ` 112 70 p
1 01 SOH 17 11 DC1 33 21 ! 49 31 1 65 41 A 81 51 Q 97 61 a 113 71 q
2 02 STX 18 12 DC2 34 22 " 50 32 2 66 42 B 82 52 R 98 62 b 114 72 r
3 03 ETX 19 13 DC3 35 23 # 51 33 3 67 43 C 83 53 S 99 63 c 115 73 s
4 04 EOT 20 14 DC4 36 24 $ 52 34 4 68 44 D 84 54 T 100 64 d 116 74 t
5 05 ENQ 21 15 NAK 37 25 % 53 35 5 69 45 E 85 55 U 101 65 e 117 75 u
6 06 ACK 22 16 SYN 38 26 & 54 36 6 70 46 F 86 56 V 102 66 f 118 76 v
7 07 BEL 23 17 ETB 39 27 ' 55 37 7 71 47 G 87 57 W 103 67 g 119 77 w
8 08 BS 24 18 CAN 40 28 ( 56 38 8 72 48 H 88 58 X 104 68 h 120 78 x
9 09 HT 25 19 EM 41 29 ) 57 39 9 73 49 I 89 59 Y 105 69 i 121 79 y
10 0A LF 26 1A SUB 42 2A * 58 3A : 74 4A J 90 5A Z 106 6A j 122 7A z
11 0B VT 27 1B ESC 43 2B + 59 3B ; 75 4B K 91 5B [ 107 6B k 123 7B {
12 0C FF 28 1C FS 44 2C , 60 3C < 76 4C L 92 5C \ 108 6C l 124 7C |
13 0D CR 29 1D GS 45 2D - 61 3D = 77 4D M 93 5D ] 109 6D m 125 7D }
14 0E SO 30 1E RS 46 2E . 62 3E > 78 4E N 94 5E ^ 110 6E n 126 7E ~
15 0F SI 31 1F US 47 2F / 63 3F ? 79 4F O 95 5F _ 111 6F o 127 7F DEL
2、Unicode 编码
正如前面所说,字符编码的种类有非常多,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。
可以想象,如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。这就是 Unicode,就像它的名字所表示的,这是一种所有符号的编码,由国际组织设计。所以 Unicode 是一个很大的集合,现在的规模可以容纳100多万个符号,每个符号的编码都不一样。
需要注意的是,Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。比如,汉字严
的 Unicode 是十六进制数4E25
,转换成二进制数足足有15位(100111000100101
),也就是说,这个符号的表示至少需要2个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。
所以就会出现两个严重的问题:第一个问题是,如何才能区别 Unicode 和 ASCII ?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果 Unicode 统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。
它们造成的结果是:1)出现了 Unicode 的多种存储方式,也就是说有许多种不同的二进制格式,可以用来表示 Unicode。2)Unicode 在很长一段时间内无法推广,直到互联网的出现。
常见Unicode编码范围:
-- 汉字:[0x4e00,0x9fa5](或十进制[19968,40869])
-- 数字:[0x30,0x39](或十进制[48, 57])
-- 小写字母:[0x61,0x7a](或十进制[97, 122])
-- 大写字母:[0x41,0x5a](或十进制[65, 90])
3、UTF-8编码
UTF-8 就是在互联网上使用最广的一种字符编码,是 Unicode 的实现方式之一。UTF-8 最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
如:在UTF-8编码中,一个英文字符占一个字节,一个中文(含繁体)占三个字节;中文标点占三个字节,英文标点占一个字节。
UTF-8 的编码规则很简单,只有二条:
1)对于单字节的符号,字节的第一位设为0
,后面7位为这个符号的 Unicode 码(因此对于英语字母,UTF-8 编码和 ASCII 码是一样的)
2)对于n
字节的符号(n > 1
),第一个字节的前n
位都设为1
,第n + 1
位设为0
,后面字节的前两位一律设为10
。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
下表总结了编码规则,字母x表示可用编码的位:
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,就表示当前字符占用多少个字节。
下面,还是以汉字严
为例,演示如何实现 UTF-8 编码:
严
的 Unicode 是4E25
(100111000100101
),根据上表,可以发现4E25
处在第三行的范围内(0000 0800 - 0000 FFFF
),因此严
的 UTF-8 编码需要三个字节,即格式是1110xxxx 10xxxxxx 10xxxxxx
。然后,从严
的最后一个二进制位开始,依次从后向前把(100111000100101
)填入格式中的x
,多出的x
位补为0
。这样就得到了,严的 UTF-8 编码:11100100 10111000 10100101
,转换成十六进制就是E4B8A5
。
从上一小节我们看到了中文在Unicode编码的范围为
[0x4e00,0x9fa5]
,然后对应上面的对照表可以看出,这个范围都是处在第三行的,所以可以说“一个中文字符在UTF-8编码中占用了3个字节”。
UTF8 和 UTF8+BOM 问题:
什么是BOM? BOM(byte of mark)是一个 Unicode 中一个特殊字符;表示0长度,非断行的空格。
BOM出现的初衷:BOM最初是用来区分UTF-16和UTF-32的;UTF-16编码使用内容“FE FF”的BOM来标记big ending,即高位结束符;用“FF EE”标记 small ending,即低位结束符。
微软记事本带来的麻烦:在 UTF-8 文件中放置 BOM 主要是微软的习惯,Windows的记事本有个臭名昭著的破毛病就是在 UTF-8 文件开头加 BOM,在不支持或者未识别utf8编码的环境下,该BOM会被解析成乱码“"”,导致问题。
其它相关类型:UTF-16(字符用两个字节或四个字节表示)、UTF-32(字符用四个字节表示)
四、参考
在线工具:Unicode 字符百科
什么是Unicode(编码标准)?
阮一峰的网络日志:ASCII,Unicode 和 UTF-8
https://home.unicode.org
字体编辑用中日韩汉字Unicode编码表