不区分大小写的字符串比较。

89
我想比较两个变量是否相同,但我希望这种比较不区分大小写。
例如,这是区分大小写的:
if($var1 == $var2){
   ...
}

但我想要这个不区分大小写,应该如何处理?

7个回答

146

这很简单,你只需要在两个变量上调用strtolower().

如果你需要处理Unicode或国际字符集,可以使用mb_strtolower().

请注意,其他答案建议使用strcasecmp()—该函数不处理多字节字符,因此任何UTF-8字符串的结果都将是虚假的。


1
一般来说,我认为MySQL字符串比较是不区分大小写的。也就是说,'A' = 'a' 是成立的。参考链接:http://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html - asthasr
4
抱歉,我不是MySQL的专家;您可能需要将其发布为另一个问题。 - asthasr
1
请注意,使用此方法可能会遇到字符集问题(例如,如果您使用一些奇怪的UTF-8字符)。在这种情况下,请改用mb_strtolower() - Balmipour
7
很遗憾,这并不简单:在Unicode中,小写变体的大写字符可能与您开始的字符不同 - 对于另一种情况也是如此。也就是说,您需要同时使用mb_strtolower()和mb_strtoupper()进行比较。 - Beat
1
@robotik 我无法编辑评论,但你可以从我的答案中复制,那里是正确的 ;) - Beat
显示剩余8条评论

77

strcasecmp()函数会忽略大小写比较两个字符串,如果它们相同则返回0,因此你可以这样使用:

if (strcasecmp($var1, $var2) == 0) {
}

3
记得测试是否等于0;这很反直觉,很容易写成 "if (strcasecmp($var1, $var2)) { ...}",但在这种情况下,0 表示相等而不是不相等,与通常情况下的 false-notequal 相反。 - Chirael
10
strcasecmp() 不支持多字节字符,因此无法处理 Unicode。 - asthasr

22

如果您的字符串使用单字节编码,那么就很简单:

if(strtolower($var1) === strtolower($var2))

如果你的字符串是UTF-8编码,你需要考虑Unicode的复杂性:大小写转换函数(to-lower-case和to-upper-case)不是双射函数。也就是说,如果你有一个小写字符,将其转换为大写,然后再转换回小写,你可能会得到不同的代码点(如果你从一个大写字符开始,情况也是如此)。

例如:

  • "İ" (拉丁字母带点的大写I, U+0130)是一个大写字符,它的小写变体是"i" (拉丁小写字母i, U+0069),而"i"的大写变体是"I" (拉丁大写字母I, U+0049)。
  • "ı" (无点的拉丁字母小写i, U+0131)是一个小写字符,它的大写变体是"I" (拉丁大写字母I, U+0049),而"I"的小写变体是"i" (拉丁小写字母i, U+0069)。

因此,mb_strtolower('ı') === mb_strtolower('i')返回false,即使它们有相同的大写字符。如果你真的想要一个不区分大小写的字符串比较函数,你必须同时比较大写和小写版本:

if(mb_strtolower($string1) === mb_strtolower($string2)
  || mb_strtoupper($string1) === mb_strtoupper($string2))

我已经在https://codepoints.nethttps://dumps.codepoints.net)对Unicode数据库进行了查询,我发现有180个代码点,在将小写字符的大写字符的小写形式和8个代码点时发现当将大写字符的小写字符的大写形式时发现有不同的字符。

但问题变得更糟了:用户看到的同一字形簇可能有多种编码方式:"ä"可以表示为Latin Small Letter a with Diaeresis (U+00E4)或为Latin Small Letter A (U+0061)Combining Diaeresis (U+0308) - 如果你在字节级别进行比较,这将不返回真值!

但是在Unicode中有一个解决方案:规范化!共有四种不同的形式:NFC、NFD、NFKC、NFKD。对于字符串比较,NFC 和 NFD 是等效的,而 NFKC 和 NFKD 是等效的。我会选择NFKC,因为它比NFKD短,并且"ff"(Latin Small Ligature ff, U+FB00)将被转换为两个普通的"f"(但是2⁵也会被扩展为25……)。

最终函数如下:

function mb_is_string_equal_ci($string1, $string2) {
    $string1_normalized = Normalizer::normalize($string1, Normalizer::FORM_KC);
    $string2_normalized = Normalizer::normalize($string2, Normalizer::FORM_KC);
    return mb_strtolower($string1_normalized) === mb_strtolower($string2_normalized)
            || mb_strtoupper($string1_normalized) === mb_strtoupper($string2_normalized);
}

请注意:

  • 您需要 intl 包才能使用Normalizer
  • 建议在使用本函数之前先检查它们是否相等^^,以优化函数。
  • 根据您的口味,您可能想使用 NFC 而非 NFKC,因为 NFKC 移除了太多格式区别。
  • 您需要自己决定是否需要这些复杂性,或者您更喜欢一个更简单的版本。

2
if(strtolower($var1) == strtolower($var2)){
}

1
虽然我理解这个技术并且它相当基础,但这不是一个慷慨或解释清楚的答案。当新研究人员看到只有代码的答案时,他们会开始认为那是Stackoverflow上完全可以接受的答案。请以教育为目的,树立更好的发布行为示范,而不是仅仅提供一个解决方案就离开。 - mickmackusa

0
// to return boolean "are input strings equal?"
function mb_strcasecmp($s1,$s2): bool {
  return 0 === mb_stripos($s1,$s2) && 0 === mb_stripos($s2,$s1);
}

这个版本支持多字节字符,但不需要intl包。

0

1
正如其他答案所指出的那样,这对于Unicode是有问题的。 - TextGeek

0
为什么不这样做:
if(strtolower($var1) == strtolower($var2)){
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接