注目イベント!
春の新人向け連載2025開催中!
今年も春の新人向け連載が始動しました!!
現場で役立つ考え方やTipsを丁寧に解説、今日から学びのペースを整えよう。
詳細はこちらから!
event banner

字符编码 必须牢记这些 ~UTF-8篇~

日本語|English|中国语
| 3 min read
Author: yoshifumi-moriya yoshifumi-moriyaの画像
Information

为了覆盖更广泛的受众,这篇文章已从日语翻译而来。
您可以在这里找到原始版本。

前言

#

这次作为面向新人的文章,我想谈谈我们这些处理日语的程序员长久以来一直深陷的“字符编码”问题。
在工作中,许多常见的故障都源于字符编码,这是无法回避的知识。

“字符编码”有着非常悠久的历史,是当时的工程师们在面对各种限制时,经过痛苦和反复试验所积累的产物。因此,单纯从现状去评价它“有点不够好”或“难以理解”是不妥当的。然而,我们无法回避这个问题。因此,本文将针对字符编码的性质以及常见的陷阱,针对不同案例给出解决方法。

上一篇继 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 等应对措施。

无论如何,仅仅统一字符集或编码并不能规避所有问题。在此前提下,请加深对所采用的字符集和编码方式的理解。
对字符编码相关的故障,只要正确理解其原理并加以应对,反过来就能成为你的技术武器。

豆蔵では共に高め合う仲間を募集しています!

recruit

具体的な採用情報はこちらからご覧いただけます。