5

我正在编写一个需要将其输入从 UTF-8 转码为 ISO-8859-1 (Latin 1) 的应用程序。

一切正常,除了我有时会得到一些变音符号的奇怪编码。例如,带有 2 个点 (0xEB) 的拉丁语 1 E 通常以 UTF-8 0xC3 0xAB 的形式出现,但有时也以 0xC3 0x83 0xC2 0xAB 的形式出现。

这从不同的来源发生了很多次,并注意到第一个和最后一个字符与我的预期相匹配,是否存在我的库不知道的编码规则?

4

3 回答 3

10

某些 Unicode 字符可以以组合分解的形式表示。例如,德语变音符号 uü可以由单个字符üu后跟表示¨,文本渲染器随后会将其组合。

有关详细信息,请参阅有关Unicode 等效性的 Wikipedia 文章。

因此,Unicode 库通常提供将字符串规范化为一种或另一种形式的方法或函数,以便您可以比较它们。

于 2012-05-18T11:11:55.003 回答
9
$ "\xC3\x83\xC2\xAB"
ë
$ use Encode

$ decode 'UTF-8', "\xC3\x83\xC2\xAB"
ë

您已经对 UTF-8 进行了双重编码。Encode::Repair是解决这个问题的一种方法。

于 2012-05-18T11:19:21.907 回答
3

(我正在回答您的主题问题,“同一字符可以有 2 种不同的 UTF-8 编码吗? ”,这与帖子中的问题有很大不同。)

(“字符”通常表示字符串元素。它在野兽中是模棱两可的,在这里使用的词不正确。用于视觉表示的 Unicode 术语,字形,是“字形”。)

是的,有多个代码点序列可以导致相同的字素。例如,两者

U+00EB  LATIN SMALL LETTER E WITH DIAERESIS

U+0065  LATIN SMALL LETTER E
U+0308  COMBINING DIAERESIS

应显示为“ë”。让我们看看你的浏览器是怎么做的:

  • U+00EB:“ë”
  • U+0065,0308:“ë”

在 UTF-8 中,这些代码点将被编码为

  • U+00EB:C3 AB
  • U+0065: 65
  • U+0308: 抄送 88

一种是使用Unicode::Normalize或将字符串规范化为两种格式之一(您的选择)NFCNFD

$ perl -MUnicode::Normalize -E'
   $x = "\x{00EB}";
   $y = "\x{0065}\x{0308}";

   say     $x  eq     $y  ?1:0;
   say NFC($x) eq NFC($y) ?1:0;
   say NFD($x) eq NFD($y) ?1:0;
'
0
1
1

UTF-8 中还有一种称为“超长”编码的东西。(特别是 UTF-8,而不是一般的 Unicode。)在 UTF-8 中,Unicode 代码点使用以下四种位模式之一进行编码:

1 0xxxxxxx
2 110xxxxx 10xxxxxx
3 1110xxxx 10xxxxxx 10xxxxxx
4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

“x”代表要编码的代码点。必须使用最短的,所以 U+00EB 将是

0000 0000 1110 1011
      --- ---- ----

   -----   ------
110xxxxx 10xxxxxx
11000011 10101011
C3       AB

但聪明的人可能会做

0000 0000 1110 1011
---- ---- ---- ----

    ----   ------   ------
1110xxxx 10xxxxxx 10xxxxxx
11100000 10000011 10101011
E0       83       AB

应用程序应拒绝 E0 83 AB(或至少将其转换为 C3 AB),但有些应用程序不会,这可能会导致安全问题。Perl 的 Encode 模块将该序列视为无效,因此对于 Perl 来说应该不是问题。

于 2012-05-18T17:08:12.547 回答