奇怪的 UTF8 字符串比较
Posted
技术标签:
【中文标题】奇怪的 UTF8 字符串比较【英文标题】:Strange UTF8 string comparison 【发布时间】:2011-04-07 21:14:19 【问题描述】:我在 UTF8 字符串比较时遇到了这个问题,我真的不知道它开始让我头疼。请帮帮我。 基本上,我从以 UTF8 编码的 xml 文档中获得了这个字符串:'Mina Tidigare anställningar' 当我将该字符串与我自己输入的完全相同的字符串进行比较时:'Mina Tidigare anställningar'(也在 UTF8 中)。结果是假的!!! 我不知道为什么。太奇怪了。有人可以帮我吗?
【问题讨论】:
在任何情况下都不会向我们展示任何实际代码。它会带走所有的悬念!而且...有人可能会不小心想出一个解决方案! 不,不要听他的!给我们看看你的代码! 'Mina Tidigare anställningar' 是一个类似于 NaN 的特殊值,它不等于自身。 :-p @James 在这种情况下,您很可能实际上在使用两种不同的编码,它们在复制它们时会自动转换。 @Lekensteyn:你的意思是“ISO-8859-1 到 UTF-8”? ASCII 没有ä
,IIRC 的表示形式。
【参考方案1】:
This seems somewhat relevant。为简化起见,有几种方法可以在 Unicode(以及因此 UTF8)中获取相同的文本:例如,ř
可以写为一个字符 ř
或两个字符:r
和 结合 ˇ
。
您最好的选择是normalizer class - 将两个字符串标准化为相同的标准化形式并比较结果。
在其中一个 cmets 中,您展示了这些字符串的十六进制表示:
4d696e61205469646967617265 20 616e7374 c3a4 6c6c6e696e676172 // from XML
4d696e61205469646967617265 c2a0 616e7374 61cc88 6c6c6e696e676172 // typed
^^-----------------^^^^1 ^^^^^^2
注意我标记的部分,显然这个问题有两个部分。
首先,请注意this question on the meaning of byte sequence "c2a0" - 出于某种原因,您的输入被转换为 XML 文件具有正常空间的不可破坏空间。请注意,在“Mina”之后的两种情况下都有一个正常的空格。不知道如何处理 php 中的 that,除了用普通空格替换所有空格。
至于第二个,就是我上面概述的情况:c3a4
是 ä
(U+00E4 "LATIN SMALL LETTER A WITH DIAERESIS" - 一个字符,两个字节),而 61
是 a
(U+0061 "LATIN SMALL LETTER A" - 一个字符,一个字节) 和 cc88
将是组合变音符号 "
(U+0308 "COMBINING DIAERESIS" - 两个字符,三个字节)。在这里,normalization library 应该很有用。
【讨论】:
在这种情况下,一个支持Unicode的字符串比较库应该能够理解c3a4 == 61cc88。但是我怀疑它会认为您的不间断空间等于正常空间。除非你告诉它忽略空格之间的差异。您需要询问您的文本编辑器、浏览器或您输入空格的任何位置,为什么将其翻译为 nbsp。 @LarsH: 强调 should - PHP 在内部使用字节,而不是字符,所以我假设你必须做Normalizer::normalize($string1) == Normalizer::normalize($string2)
,或者规范化字符串当你加载它们时。
@Piskvor:是的...我并不是要暗示 PHP 的内部字符串比较例程是 Unicode 感知的。
@LarsH:更糟糕的是——PHP 的大多数内部函数都对字节进行操作(我可以忍受),但有些对字符进行操作,其中字符集显然受月相的影响(它是在 php.ini 深处的某个地方,我怀疑在某些情况下会有轻微的错误)。如果你能帮上忙,不要在 PHP 中对字符串做任何超出连接的操作,即使那样也要小心。
@Piskvor 这不准确。那是一些依赖于语言环境的功能。不幸的是,手册有时会忽略这些信息...【参考方案2】:
让我们盲目地尝试一下:也许两个 UTF-8 字符串的底层表示不同(您可以将带有重音符号的字符作为序列或作为唯一字符)。您应该使用 UTF8 字符串的一些十六进制转储,有人可能会提供帮助。
【讨论】:
Hej hej kriss,谢谢。这是来自 xml 文件“4d696e6120546964696761726520616e7374c3a46c6c6e696e676172”的 str 的十六进制转储。这是我自己输入的字符串'4d696e61205469646967617265c2a0616e737461cc886c6c6e696e676172'。 显然它们是不同的......问题似乎出在您自己输入的字符串中。在 xml 字符串中,您得到 20(空格),但在文件 c2a0 中(无论如何?我应该解码)。但显然不一样。【参考方案3】:mb_detect_encoding($s, "UTF-8") == "UTF-8" ? : $s = utf8_encode($s);
【讨论】:
以上是关于奇怪的 UTF8 字符串比较的主要内容,如果未能解决你的问题,请参考以下文章