文件编码格式浅析

准备一期团队内部的分享,专业领域知识之前已经讲过比较多了,这次打算准备一点比较基础也很容易混淆的知识点:文件编码格式。自身知识储备也有限,只能从比较浅的层面来入手,一点点扩展。

文件分类

如果用简单直接的方式我们可以把文件分为两类

  • 文本类型文件
  • 其他类型文件

文本类型文件的编码是逐字符进行的,每个字符的编码在任何语言任何类型计算机上的解释结果都是一样的,文本类型文件是本文分析的重点,后续会针对不同的编码方式进行详细的分析

其他类型的文件,比如png,zip,可执行程序等则需要依赖于特定的环境和对应的应用程序来解析,其编码格式也并不是逐字符进行的,后续我们会找几个文件具体分析一下

文本类型文件

简单来说,文本文件是基于字符编码的文件,每个字符对应一个固定的编码,顺序流式存取,在任何操作系统下的解释和编码结果都是一致的,文本文件除了所包含的字符以外没有任何其他信息。
常用文本编码类型有:

  • ASCII
  • GB2312
  • Unicode
  • UTF-8
  • UTF-16

一个个来看

ASCII

American Standard Code for Information Interchange,美国信息交换标准代码
ASCII编码方案一共规定了128个字符对应的二进制表示,只占用了一个字节的后面7bit,最高位为0

ASCII码表

0 1 2 3 4 5 6 7 8 9 A B C D E F
NUL 0000 SOH 0001 STX 0002 ETX 0003 EOT 0004 ENQ 0005 ACK 0006 BEL 0007 BS 0008 HT 0009 LF 000A VT 000B FF 000C CR 000D SO 000E SI 000F
DLE 0010 DC1 0011 DC2 0012 DC3 0013 DC4 0014 NAK 0015 SYN 0016 ETB 0017 CAN 0018 EM 0019 SUB 001A ESC 001B FS 001C GS 001D RS 001E US 001F
SP 0020 ! 0021 " 0022 # 0023 $ 0024 % 0025 & 0026 ' 0027 ( 0028 ) 0029 * 002A + 002B , 002C - 002D . 002E / 002F
0 0030 1 0031 2 0032 3 0033 4 0034 5 0035 6 0036 7 0037 8 0038 9 0039 : 003A ; 003B < 003C = 003D > 003E ? 003F
@ 0040 A 0041 B 0042 C 0043 D 0044 E 0045 F 0046 G 0047 H 0048 I 0049 J 004A K 004B L 004C M 004D N 004E O 004F
P 0050 Q 0051 R 0052 S 0053 T 0054 U 0055 V 0056 W 0057 X 0058 Y 0059 Z 005A [ 005B \ 005C ] 005D ^ 005E _ 005F
` 0060 a 0061 b 0062 c 0063 d 0064 e 0065 f 0066 g 0067 h 0068 i 0069 j 006A k 006B l 006C m 006D n 006E o 006F
p 0070 q 0071 r 0072 s 0073 t 0074 u 0075 v 0076 w 0077 x 0078 y 0079 z 007A { 007B | 007C } 007D ~ 007E DEL 007F
➜  Encoding git:(master) ✗ hexdump -C abcABC_ASCII.txt
00000000  61 62 63 41 42 43                                 |abcABC|
00000006

➜  Encoding git:(master) xxd -b abcABC_ASCII.txt
00000000: 01100001 01100010 01100011 01000001 01000010 01000011  abcABC

GB2312

GB2312编码是第一个汉字编码国家标准,由中国国家标准总局1980年发布,1981年5月1日开始使用。GB2312编码共收录汉字6763个,其中一级汉字3755个,二级汉字3008个。同时,GB2312编码收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个全角字符。

分区表示

GB2312编码对所收录字符进行了“分区”处理,共94个区,每区含有94个位,共8836个码位。这种表示方式也称为区位码。
01-09区收录除汉字外的682个字符。
10-15区为空白区,没有使用。
16-55区收录3755个一级汉字,按拼音排序。
56-87区收录3008个二级汉字,按部首/笔画排序。
88-94区为空白区,没有使用。
举例来说,“啊”字是GB2312编码中的第一个汉字,它位于16区的01位,所以它的区位码就是1601。

双字节编码
GB2312规定对收录的每个字符采用两个字节表示,第一个字节为“高字节”,对应94个区;第二个字节为“低字节”,对应94个位。所以它的区位码范围是:0101-9494。区号和位号分别加上0xA0就是GB2312编码。例如最后一个码位是9494,区号和位号分别转换成十六进制是5E5E,0x5E+0xA0=0xFE,所以该码位的GB2312编码是FEFE。

GB2312编码范围:A1A1-FEFE,其中汉字的编码范围为B0A1-F7FE,第一字节0xB0-0xF7(对应区号:16-87),第二个字节0xA1-0xFE(对应位号:01-94)

码表
http://tools.jb51.net/table/gb2312

我们可以看到GB2312的编码范围为:A1A1-FEFE(10100001 10100001-11111110),上文ASCII编码范围:00-7F(00000000-01111111)
从ASCII的编码区间可以看出,单字节的高位没有用,一直为0,GB2312编码的高位一直为1,因此GB2312可以完全兼容ASCII,两者同时存在一个文件编码中。

➜  Encoding git:(master) ✗ hexdump -C abcABC_GB2312.txt
00000000  61 62 63 41 42 43 c4 e3  ba c3                    |abcABC....|
0000000a

以上为“abcABC你好”的GB2312的编码数据,“abcABC”还是按照ASCII的编码方式处理:61 62 63 41 42 43,“你好”按照GB2312的编码方式:c4e3 bac3

Unicode

Unicode是一个全球统一的编码字符集,并不是一种编码方式,Unicode为每个字符分配唯一的字符编号,覆盖全球所有的语言和符号。Unicode用 U+紧跟着十六进制数表示。所有字符按照使用上的频繁度划分为 17 个平面(编号为 0-16),即基本的多语言平面和增补平面。基本的多语言平面(英文为 Basic Multilingual Plane,简称 BMP)又称平面 0,收集了使用最广泛的字符,代码点从 U+0000 到 U+FFFF,每个平面有 216=65536 个码点;增补平面从平面 1~16,分为增补多语言平面(平面 1)、增补象形平面(平面 2)、保留平面(平 3~13)、增补专用平面等,每个增补平面也有 216=65536 个码点。所以 17 个平总计有 17 × 65,536 = 1,114,112 个码点

Unicode 字符集中的字符可以有多种不同的编码方式,如 UTF-8、UTF-16、UTF-32等。这里的 UTF 是 Unicode transformation format 的缩写,即统一码转换格式,将 Unicode 编码空间中每个码点和字节序列进行一一映射的算法。

UTF-8

UTF-8是目前应用最广泛的一种Unicode编码方式,UTF-8是一种变长的编码方式,一般用1~4个字节序列来表示Unicode字符。
UTF-8的编码规则如下:

  • 如果首字节以0开头,表示单字节编码,因此UTF-8完全兼容ASCII的编码方式U+0000到U+007F;
  • 如果首字节以110开头,表示双字节编码;
  • 如果首字节以1110开头,表示三字节编码;
  • 如果首字节以11110开头,表示四字节编码;
  • 如果首字节以111110开头,表示五字节编码;
  • 如果首字节以1111110开头,表示六字节编码;
  • 对于多字节编码的字符,后续字节以10开头,如:1110xxxx10xxxxxx10xxxxxx
➜  Encoding git:(master) ✗ hexdump -C abcABC_UTF8.txt
00000000  61 62 63 41 42 43 e4 bd  a0 e5 a5 bd c2 ae        |abcABC........|
0000000e
➜  Encoding git:(master) xxd -b abcABC_UTF8.txt
00000000: 01100001 01100010 01100011 01000001 01000010 01000011  abcABC
00000006: 11100100 10111101 10100000 11100101 10100101 10111101  ......
0000000c: 11000010 10101110                                      ..

以上为“abcABC你好®”的UTF-8的编码数据,“abcABC”依然按照ASCII的编码方式采用单字节,0开头:01100001 01100010 01100011 01000001 01000010 01000011
“你”采用三字节编码:11100100 10111101 10100000
“好”也是三字节编码:11100101 10100101 10111101
“®”为双字节编码:11000010 10101110

其他类型文件

把文本文件之外的其他文件归为一类是因为这些文件并不是按字符进行的统一编码,每种类型的文件都有自己特定的编码方式。种类非常多,各有各的不同。此类文件通常习惯把文件最开始的几个字节作为文件类型的标识,比如:

  • JPEG (jpg),文件头:FFD8FF
  • PNG (png),文件头:89504E47
  • GIF (gif),文件头:47494638
  • PDF(pdf),文件头: 255044462D312E
  • AVI (avi),文件头:41564920
  • ZIP Archive (zip),文件头:504B0304
  • RAR Archive (rar),文件头:52617221

我们验证几个文件头:

ZIP
50 4b 03 04
➜  Encoding git:(master) ✗ hexdump abcABC_UTF8.txt.zip
0000000 50 4b 03 04 14 00 08 00 08 00 a5 bc b0 4c 00 00
0000010 00 00 00 00 00 00 00 00 00 00 0f 00 10 00 61 62
0000020 63 41 42 43 5f 55 54 46 38 2e 74 78 74 55 58 0c
0000030 00 d7 51 fc 5a 26 50 fc 5a f5 01 14 00 4b 4c 4a
0000040 76 74 72 7e b2 77 c1 d3 a5 7b 0f ad 03 00 50 4b
JPG
ff d8 ff
➜  Encoding git:(master) ✗ hexdump jpg.jpg|head -100
0000000 ff d8 ff e0 00 10 4a 46 49 46 00 01 01 00 00 c8
0000010 00 c8 00 00 ff e1 00 98 45 78 69 66 00 00 4d 4d
0000020 00 2a 00 00 00 08 00 06 01 06 00 03 00 00 00 01
0000030 00 02 00 00 01 12 00 03 00 00 00 01 00 01 00 00
0000040 01 1a 00 05 00 00 00 01 00 00 00 56 01 1b 00 05
0000050 00 00 00 01 00 00 00 5e 01 28 00 03 00 00 00 01
0000060 00 02 00 00 87 69 00 04 00 00 00 01 00 00 00 66
0000070 00 00 00 00 00 00 00 c8 00 00 00 01 00 00 00 c8
PDF
25 50 44 46 2d 31 2e
➜  Encoding git:(master) ✗ hexdump pdf.pdf|head -100
0000000 25 50 44 46 2d 31 2e 33 0a 25 c4 e5 f2 e5 eb a7
0000010 f3 a0 d0 c4 c6 0a 34 20 30 20 6f 62 6a 0a 3c 3c
0000020 20 2f 4c 65 6e 67 74 68 20 35 20 30 20 52 20 2f
0000030 46 69 6c 74 65 72 20 2f 46 6c 61 74 65 44 65 63
0000040 6f 64 65 20 3e 3e 0a 73 74 72 65 61 6d 0a 78 01
0000050 65 8f cd 0a c2 40 0c 84 ef 7d 8a 39 ea c1 75 b3

Base64

Base64并不会用来进行文件的编码,但是开发过程中会经常用到。这里也单独介绍一下。

简单讲Base64编码是从二进制到字符的过程,可以把二进制数据编码为可见的字符数据。可以用作HTTP POST和HTTP GET URL中的参数。

Base64编码后的数据是一个字符串,其中包含的字符为:A-Z、a-z、0-9、+、/共64个字符:26 + 26 + 10 + 1 + 1 = 64 另外还有一个补位用的填充字符“="。

Base 64
0(A) 10(Q) 20(g) 30(w)
1(B) 11(R) 21(h) 31(x)
2(C) 12(S) 22(i) 32(y)
3(D) 13(T) 23(j) 33(z)
4(E) 14(U) 24(k) 34(0)
5(F) 15(V) 25(l) 35(1)
6(G) 16(W) 26(m) 36(2)
7(H) 17(X) 27(n) 37(3)
8(I) 18(Y) 28(o) 38(4)
9(J) 19(Z) 29(p) 39(5)
a(K) 1a(a) 2a(q) 3a(6)
b(L) 1b(b) 2b(r) 3b(7)
c(M) 1c(c) 2c(s) 3c(8)
d(N) 1d(d) 2d(t) 3d(9)
e(O) 1e(e) 2e(u) 3e(+)
f(P) 1f(f) 2f(v) 3f(/)

总共64个字符需要用6位来表示,表示成数值为0~63
我们还是用“abcABC”来看一下编码的过程:
“abcABC”对应的ASCII码为:

01100001 01100010 01100011 01000001 01000010 01000011  abcABC

总共 8 * 6 = 48位,转换为6位一个字节48 = 6 * 8

按照6位一个字节分割之后:

011000 010110 001001 100011 010000 010100 001001 000011

转为16进制:

18 16 9 23 10 14 9 3

对应到码表:

YWJjQUJD

Base64一个应用

![Pic Base64 Encoding][test_img]
[test_img]: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAMAAAANIilAAAAAllBMVEX6VW7////6XnX//P36V2/6TWf6UGr/9PX6SmX/4+f/+/v/2+H+2N3/9vf/7e//5ur/3+T6SGP/+Pn6UWv/8fP/7/L/6u3/+fr+0Nf+zdX6RWD/6Ov6ZHv7Zn3/4eX6NlT+xs7+w8z+1dv+ytL6O1j+0tn9usX6PVr7eo37cIX8qLX9vsj9tL/8k6P8nKv8iJr6a4H5Lk3nriMCAAACpUlEQVRIx+3X2Y6bMBSAYQxmsw02a0xI2Mk201ne/+VKHJJohgNRJzeV2v8WfTpCgHzQtvqP22pPpT9B/+PnsGlal0y4WYzxvjOtIIjGrGlmt8cYwjgqjzvhNrvT6+6lrtpM9g1jlFIhisK9JF4OZYQhXP5CqrSlBU8T2yMI6FcJ4f0RjbG6Z1S4PPWT2La9cEUMw7jpQwfgskJjdiUXsOyiCTZN9349W8CJ5kxxxNG1fgnHGMDWbbKxONk1zZ/j2rEWMWuEm/vJ4MiI7qUn7cHktqGi4Pk4+jybGEPIiGm7wzA2VIgco8217SX9I2NyqKpZ/QDrZaBa3+qcimVDshcViIsrXm3X2rccXRZM5bYzmKhg3HCqyjMQC7RSoRDCLBUqX87gUIU8CFPfVcUNiCnyVMiGsIi5ymZ/jAO98HKVR2ewrUIxhN3QVxEKfhgMxSqSbADMSaJCYgYnqtCHcG7EKlSAuEG+ykshnF5vygVxT1KVnW+CKfYXcSRDrko4hJPrs+AgzmxX5RcwDlUzuE2EKhcQjq8vbw7iKqUql8KYqEBsRTVnKtFA2L5+sCmIX4peyizLWIa7YCjClyMscBxnrz3AO1pXmZSSZR+RPjRoZfUha/MAv7aNvzLIkH3OeyvPuHyPaSOrlqAxH8SnHH3tWGLcbUKS8oKxZRzsbsowDLLyUBqsgzVFhaAsk8t4zb7iMET15+cr4i6E4YPujr0Vej+gZMQGGktwMMXmBNseCe38gjMCH7FjZfsde7Fn+6nCsorRWN9F0FoxwXacjLiv2X2twPBCM80I7djPXcHaHKneyplV6lBz/1w6lHPO3UJQyhqZtVW9O9XUz6tDaWF4iev2EZ7PMbG27zRwiVPDTed75i3LdMwI/5Vb77+Bn+mp38HfQN1OMU+bDU4AAAAASUVORK5CYII=

把图片序列化之后用Base64编码得到的数据可以直接嵌入到Html标签或者Markdown文档中来显示,受本文所用的Markdown解析器的限制,这里只能看到编码后的结果不能解析为图片。

你可能感兴趣的:(文件编码格式浅析)