字符编码 必须牢记这些 ~UTF-8篇~
Back to Top
为了覆盖更广泛的受众,这篇文章已从日语翻译而来。
您可以在这里找到原始版本。
前言
#这次作为面向新人的文章,我想谈谈我们这些处理日语的程序员长久以来一直深陷的“字符编码”问题。
在工作中,许多常见的故障都源于字符编码,这是无法回避的知识。
“字符编码”有着非常悠久的历史,是当时的工程师们在面对各种限制时,经过痛苦和反复试验所积累的产物。因此,单纯从现状去评价它“有点不够好”或“难以理解”是不妥当的。然而,我们无法回避这个问题。因此,本文将针对字符编码的性质以及常见的陷阱,针对不同案例给出解决方法。
上一篇继 Shift_JIS 之后,本次将解说字符编码“Unicode”中的“UTF-8”,介绍你必须掌握的要点。
本次想说的要点
#- 已经该放弃 Shift_JIS 之类,统一使用 Unicode 了吧
- 使用 Unicode 时需要注意的要点
- 关于 UTF-8 中的 BOM
什么是 Unicode
#Unicode 如今已广泛普及,可谓是世界标准,在 Java 内部也使用它,因此成为无法回避的字符编码。
准确来说,Unicode 是“字符集”,其目的是处理世界上所有字符(英数字、符号、平假名、片假名、汉字等)。每个字符都被分配了称为“码点(code point)”的唯一编号,例如“ A ”(半角 A)对应的码点是 U+0041
,“あ”对应 U+3042
。
如果 Unicode 真能处理所有字符,就没必要再使用 Shift_JIS 之类的,完全统一为 Unicode 仿佛就能解决字符编码问题。
然而,实际上采用 Unicode 并不能解决一切问题。
这次将解说 Unicode 的一种编码方式“UTF-8”可能出现的问题点。
什么是 UTF-8
#在具体说明问题点之前,先简单介绍一下作为 Unicode 编码方式的“UTF-8”。
UTF-8 这种编码方式将 Unicode 的码点范围(U+0000
- U+10FFFF
)表示为可变长度的 1~4 字节。例如,“A”(U+0041)和“あ”(U+3042)在 UTF-8 中表示如下:
- “A”(
U+0041
):0x41
(1 字节) - “あ”(
U+3042
):0xE3 0x81 0x82
(3 字节)
在 UTF-8 中,所需字节数会根据码点值而变化。
- 1 字节:
U+0000
-U+007F
(ASCII) - 2 字节:
U+0080
-U+07FF
- 3 字节:
U+0800
-U+FFFF
(大多数日文字符包含在此) - 4 字节:
U+10000
-U+10FFFF
(辅助平面的字符)
因此,不同字符在 UTF-8 中的字节序列长度会不同,这是它的一个特点。
此外,UTF-8 中用 1 字节表示的字符与 ASCII 兼容,并且在互联网中作为事实标准被广泛采用。
什么是 BOM
#在说明本次的问题点之前,也先介绍一下“BOM”。
BOM(Byte Order Mark)是附加在文本开头,用于指示字节顺序(大小端)的标记。
在 UTF-16 或 UTF-32 等编码方式中,需要指定字节的排列顺序。这个字节顺序被称为“端序”(Endian)。
例如,字符“A”(U+0041)在 UTF-16 中的表示如下:
- 大端(BE):
0x00 0x41
(先写高位字节) - 小端(LE):
0x41 0x00
(先写低位字节)
UTF-8 中的 BOM
#由于 UTF-8 是与端序无关的编码方式,因此通常不需要附加 BOM。不过,也有人为了明确文件为 UTF-8 而特意添加 BOM。
一个常见的例子是,当 Excel 将 CSV 文件输出为 UTF-8 格式时,会自动附加 BOM。正因为如此,往往在各种场景中引发问题。
例如,JSP 文件或 Thymeleaf 模板会生成 HTML 响应。但是,如果以附带 UTF-8 BOM 的方式编写,那么在 HTML 顶部应有的 <!DOCTYPE html>
之前会输出 0xEF 0xBB 0xBF
。某些浏览器可能因此无法将其正确解释为 HTML。
此外,在读取 CSV 文件时也需注意。
如果代码没有考虑到 BOM 的存在,文件开头的几个字节可能被当作乱码,或无法正确读取表头名称,从而导致错误。
BOM 难以处理的原因
#这个问题的棘手之处在于,BOM(Byte Order Mark)在编辑器中是看不见的。
因此,当在 JSP 文件或 CSV 文件中发生错误时,很难察觉文件开头包含了 BOM,导致定位原因的过程常常延迟。
阅读本文的各位,在 UTF-8 文件出现意外问题时,也请从“文件开头是否包含 BOM”这一角度进行排查。
例如,可以使用二进制编辑器或 certutil -dump sample.csv
命令等,检查文件开头是否包含 0xEF 0xBB 0xBF
。
对该问题的应对措施
#那么,如果已确认 BOM 是问题的根源,可采取的应对措施大致可归为以下两种。
- 删除 BOM
- 跳过 BOM
删除 BOM
#这适用于 JSP 或 Thymeleaf 等模板的场景。
由于不期望的 BOM 位于首部,妨碍了浏览器对 HTML 的解析,因此请删除 BOM。
此外,如果要指定 HTML 的编码,建议在头部标签中使用 <meta charset="utf-8">
。
跳过 BOM
#也可以选择跳过非预期的 BOM。这是在实现文件读取端的应对措施。
例如,在一个需要导入用户生成的 UTF-8 CSV 的系统中,无法限制用户使用 Excel 等生成附带 BOM 的 CSV 文件。
如果希望对这类外部输入进行灵活处理,可在实现时假设文件开头可能包含 BOM,并在检测到时跳过它,从而解决问题。
总结
#- BOM 容易成为看不见的“地雷”,因此在出现问题时,它是首先需要检查的要点之一。
- 对于应用程序自身提供的资源,应删除 BOM。
- 对于作为外部输入且难以由应用程序方控制的情况,应以可能会引入 BOM 为前提,考虑跳过 BOM 等应对措施。
无论如何,仅仅统一字符集或编码并不能规避所有问题。在此前提下,请加深对所采用的字符集和编码方式的理解。
对字符编码相关的故障,只要正确理解其原理并加以应对,反过来就能成为你的技术武器。