同一个角色可以有2种不同的UTF-8编码吗?

时间:2012-05-18 11:06:51

标签: perl utf-8 character-encoding

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

一切正常,除了我有时会对某些变音符号进行奇怪的编码。例如,带有2个点(0xEB)的Latin 1 E通常为UTF-8 0xC3 0xAB,但有时也为0xC3 0x83 0xC2 0xAB。

这种情况多次来自不同的来源并注意到第一个和最后一个字符符合我的预期,是否存在我的图书馆不了解的编码规则?

3 个答案:

答案 0 :(得分:9)

某些Unicode字符可以用撰写的分解的形式表示。例如,德语变音符号ü可以由单个字符üu后跟¨表示,然后文本渲染器将合并。< / p>

有关血腥的详细信息,请参阅有关Unicode equivalence的维基百科文章。

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

答案 1 :(得分:9)

$ "\xC3\x83\xC2\xAB"
ë
$ use Encode

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

你有双重编码的UTF-8。 Encode::Repair是解决这个问题的一种方法。

答案 2 :(得分: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:CC 88

可以使用Unicode::NormalizeNFCNFD将字符串规范化为两种格式之一(您的选择)。

$ 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的问题。