文本文件与字符编码(ASCII、UTF)

                     

贡献者: addis

1. 文本文件

图
图 1:Windows 11 系统的写字板(Notepad,黑色主题),下方显示使用 UTF-8 编码,CRLF 换行符。

   我们这里讨论的是文本文件(text file)。例如 Windows 中常见的 txt 拓展名文件就是文本文件。文本文件只能储存字符,也就是说文件中的数据只能是 “有几个字符” 以及 “每个字符是什么”。所以文本文件本质上不包括字体,字号,下划线等等的其他信息。一个文件是否是文本文件是由它的编码格式决定的,而不是由拓展名决定的。一些其他的文件拓展名如 html,xml,md,以及大部分编程语言的代码文件都是文本文件。

   我们把所有非文本文件统称为二进制文件(binary file),因为从原理上来说任何文件都是以二进制的形式储存的1。例如 Word 文档保存的 doc 或 docx 拓展名文件就是二进制文件,因为里面用其特定的格式储存了许多其他信息。 又例如图片文件 jpg, png,视频文件 mp4,zip 压缩文件等也都是二进制文件。

   文本文件的一个基本的问题是,字符以什么形式保存?从概念上来说,文本文件中每个字符对应一个非负整数,我们把对应的规则叫做编码(encoding),一个文本文件一般只使用一种编码。例如在著名的 ASCII(读作 asky)编码中,128 个字符被一一对应到 0-127 的整数2(见图 3 ),这些字符包括大写和小写字母,数字,常见标点,以及一些格式上的符号如空格,换行符,制表符等。ASCII 编码可以满足通常的英语写作需求,但显然不支持其他语言如中文。下文会看到中文的文本文档通常以 UTF-8(Unicode 的一种)或者 GBK 编码储存。GBK 是 GB2312 的拓展,并向后兼容3

   遗憾的是,一般情况下文本文档中不会声明使用的编码,所以如果储存和打开文件的编码方式不一致,就会导致显示乱码(例如著名的 “锟斤拷”)。尽管现在大部分系统和软件都用 Unicode 尤其是 UTF-8 编码保存和打开文本文件,但仍然有一些软件为了兼容性使用以前的编码,例如 Windows 写字板(Notepad,不建议使用)默认将中文保存为 GBK(即中文环境下所谓的 ANSI)编码。

   如果经常编辑文本文档(尤其是编程),我们推荐使用一款功能较完善的编辑器,例如 Visual Studio Code(简称 VScode4,Sublime 等5。这里我们使用 VScode 编辑器为例进行介绍。

   当 VScode 打开一个文本文档时,会尝试自动检测编码,并显示在右下角的状态栏图 2

图
图 2:VScode 的右下角状态栏显示 Tab 字符的显示长度为 4 个空格,编码为 UTF-8,换行符为 LF(见下文),文件格式为文本文档

   如果这时编辑器中仍然显示乱码,说明自动检测失败,可以尝试手动切换不同编码打开文件:点击状态栏上显示的编码,选择 “Reopen with Encoding”,选择需要的编码即可。以正确的编码打开后,如果想改为别的编码,就选 “Save with Encoding”。

   如果你使用 Linux 命令行,那么 dos2unix 命令也常用于检测换行符和 BOM 等信息,以及是文本文件还是二进制文件。用法是 dos2unix -ih 文件1 文件2...,输出结果每个文件一行,格式为 DOS UNIX MAC BOM TXTBIN FILE,分别是 CRLF的个数 LF的个数 CR的个数 是否有BOM 文本还是二进制 文件名dos2unix 文件1 文件2... 还可以自动删除 BOM。

2. 判断文本文件和二进制文件

   如果一个文件中出现了任意 NUL 字符,那么它很有可能是二进制文件。如果出现了许多 NUL 字符,那么几乎肯定是二进制文件。NUL 字符是 ascii 表中编号为 0 的字符,也就是说在该子节中,所有的比特都为零。NUL 字符通常用于 C 语言的字符串的最后代表字符串结束,但几乎不用于文本文件中(无论使用哪种编码)。

   许多计算机程序以此为标准区分文本文件和二进制文件。

3. 换行符

   除了编码外,文本文件的换行符使用也有不同的规范。历史上,人们在使用机械打字机时,当一行打完,打字头停留在页面某一行的最右端。回车键(carrage return,简写为 CR)用于将打字头移动到页面的最左端,而 LF(line feed)键用于将纸张上移一行。在 ASCII 编码中,CR 和 LF 分别对应 13 和 10。Windows 系统沿用了这个传统,所以当我们在写字板程序中按下回车时,分别会在文件中插入 CR 和 LF 两个字符。Linux 系统默认使用单个 LF 换行,而老版本的 Mac 则默认用单个 CR 换行(OS X 及以后也用单个 LF)。所以同一个文件在不同系统的默认编辑器中可能会出现换行符识别不出导致全部文字排成一行的情况。一般来说,同一个文本文件应该使用同一个规范。

   无论在哪个系统中使用 VScode,它在打开文本文件时都会自动检测换行符并显示在图 2 的状态栏中6。若要将改变换行符,点击当前显示的换行符并选择想要的即可。

4. ASCII 编码

图
图 3:ASCII 表(来自 Wikipedia)各列分别是:十进制、十六进制、二进制、八进制、表示的字符

   其他绝大部分编码(如 Unicode,GBK)的前 128 个数字代表的字符都与 ASCII 相同,所以一个 ASCII 编码的文件用任何编码方式打开的结果都极有可能是相同的。

5. Unicode 编码

   Unicode 编码可以同时支持许多(人类)语言的字符,还包含一些图形(如箭头,多边形,数学符号等等)和 emoji 表情。它将每个字符和 0-1114111(十六进制的 10FFFF)范围的整数(叫做 code point)一一对应。Unicode 又分为几种不同的具体编码类型,它们决定了具体如何把每个 code point 表示成若干个字节:UTF-32 是定长度的,即将每个字符(对应的整数)都储存为 4 个字节(32 比特)7。最常见的 UTF-8 是变长度的,即如果字符对应的数字较小,就储存为 1 个字节(8 比特),随着数字变大,可以最多储存为 4 个字节。UTF-16 也是变长度的,原理和 UTF-8 类似,但一个字符最少对应两个字节。

   如今 UTF-8 是使用最广泛的文本文档编码,我们推荐尽可能使用 UTF-8 储存文本文档。由于 UTF-8 最开始的 127 个 code point 和 ASCII 编码相同,所以与之向后兼容。也就是说所有 ASCII 编码的文件也可以看成时 UTF-8 编码的(反之不行)。

Byte Order Mark

   在 Unicode 编码的文件开头,有一些软件会插入(或需要)2 到 3 个字节的 BOM(byte order mark),这样一是可以表明文件使用 Unicode 编码,二来可以表明字节排列顺序。当一个整数需要用多个字节表示时,不同的系统可能会把这些字节倒置,即所谓的 big endian 和 small endian。

   一般来说,UTF-8 如无必要不推荐使用 BOM(0xEFBBBF)。UTF-16 中,0xFFFE 表示 little-endian,0xFEFF 表示 big-endian。0xFFFE 表示 big-endian。UTF-32 中 BOM 0xFFFE0000 表示 little-endian,0x0000FEFF 表示 big-endian。

   VScode 同样可以自动检测文件是否存在 BOM,如果有则显示在状态栏的编码中,例如 UTF-8 with BOM

UTF-8 编码规则

   每个 code point 使用 1-4 个字节表示

  

未完成:然后呢?


1. ^ 虽然文本文件也是以二进制储存,但一般不叫做二进制文件。
2. ^ 另外有 extended ASCII 编码对应 0-255 的整数
3. ^ 也就是说 GB2312 编码的文件也可以看作是 GBK 编码的
4. ^ 注意 Visual Studio Code 不是 Visual Studio,是一个文本编辑器,可以通过安装插件拓展许多功能。
5. ^ 比较硬核的程序员也会使用 Vim 和 Emacs 作为主要编辑器。另外 Notepad++ 也较为流行,但由于其开发者发表了不言论而被一些用户制。
6. ^ 一般不会检测失败,除非不同的换行符格式混用
7. ^ 注意 UTF-32 编码比 UTF-8 和 UTF-16 要少见得多。

                     

© 小时科技 保留一切权利