奇怪的 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" - 一个字符,两个字节),而 61a (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 字符串比较的主要内容,如果未能解决你的问题,请参考以下文章

utf8字符集下的比较规则

php中的字符串比较奇怪的行为

javascript奇怪的字符串比较[重复]

奇怪的错误,将字符与字符串进行比较(p == "cancel")

奇怪的PHP字符串整数比较和转换

phpmyadmin 显示奇怪的字符(utf8)