我有一个包含 EM Dash(—或 HTML 中的 —
)的 ASCII 文件,十六进制值为 0x97。当我们通过某个应用程序传递此文件时,它会以 UTF-8 形式到达,并将字符转换为 0xC297,即 HTML 中的 —
。然而,当我们通过另一个应用程序传递此文件时,它将字符转换为 0xE28094 或 HTML 中的 —
。
是什么原因导致这些应用程序不同地转换这些字符?可能是代码页设置吗?
— 是错误的。当使用数字字符引用时,该数字指的是Unicode代码点。对于256以下的数字,其代码点与ISO-8859-1中的代码点相同。在8859-1中,字符151属于“C1控制码”,而不是破折号或任何其他可见字符。
混淆的原因是,在Windows代码页1252(西欧)中,字符151是一个破折号。许多人认为cp1252与ISO-8859-1是相同的东西,但实际上并不是:C1范围(128至159)内的字符是不同的。
第一个应用程序是将您的“ASCII”文件*读取为ISO-8859-1,但实际上它可能是cp1252,您需要一种方法来提示应用程序所期望的编码方式。
(*:“ASCII”是一个误称,如果文件中有设置了最高位的字符,则应该用“ANSI”代替。在Windows世界中,“ANSI”通常意味着“以当前系统默认代码页编码的文本”,虽然这个说法也是不准确的。)
—
并不是表示破折号的字符,你的文本被误译为这个值了。—
是表示破折号的HTML十进制实体。具体来说,它引用了Unicode代码点8212,代表破折号。你的第一个应用程序……
数据最初是以w-1252编码的破折号形式存在的。在w-1252中,破折号映射到十进制值151(十六进制为0x97,二进制为10010111)。
某些情况下,破折号会被处理代码视为iso-8859-1编码的文本中的字节。当代码将0x97解释为字符串/字符时,它会根据iso-8859-1编码将0x97映射为一个字符。在iso-8859-1中,0x97映射到字符"End of guarded area"。
接下来,代码将被视为“End of guarded area”控制字符的字符串被编码为utf-8格式。用utf-8编码表示的“End of guarded area”是两个字节的序列:0xC2 0x97。
你的第二个应用程序……
文本文件已正确解释为w-1252,因此0x97被识别为破折号,并在utf-8中正确编码为破折号:0xE2 0x80 0x94。影响这种行为的因素
不确定您是否正在处理Web应用程序,但无论是什么概念都应该是相同的。我们在一个Web应用程序中遇到了相同的0x97->0xC297情况,人们通过表单输入数据。我发现网页的字符集声明为iso8859-1,浏览器处理w1252字符的最佳方式是将它们作为iso字节发送而不通知用户或服务器。服务器接收到数据认为它是iso并转换为utf-8,导致0xC297。
基本上,每当应用程序涉及文本时,都需要告诉它文本如何编码,否则它可能会回退到系统默认设置。如果发生这种情况,您就有可能面临数据损坏。
ASCII 文件不能包含字符 0x97,因为 ASCII 字符集的范围仅从 0x00 到 0x7F。因此您的文件不是 ASCII,而是其他单字节编码。例如,windows-1250 编码具有 0x97 的 em-dash。
如果应用程序使用与创建文件时使用的编码不同的编码解码文本文件,则任何大于 0x7F 的字符都将是错误的。
在 Unicode 中, em-dash 的字符代码为 0x2014,或者十进制的 8212。
在使用 windows-1250 编码的网页中,代码 —
将呈现为 em-dash:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>em-dash</title>
<meta http-equiv="content-type" content="text/html; charset=windows-1250"/>
</head>
<body>
<div>—</div>
</body>
</html>