如何进行不区分大小写的字符串比较?

How do I do a case-insensitive string comparison?

提问人:Kozyarchuk 提问时间:11/26/2008 最后编辑:Karl KnechtelKozyarchuk 更新时间:11/12/2022 访问量:1148689

问:

如何在 Python 中以不区分大小写的方式比较字符串?

我想使用简单的 Python 代码将常规字符串与存储库字符串的比较封装起来。我还希望能够使用常规的 python 字符串在按字符串哈希处理的字典中查找值。

python 比较 不区分大小写

评论


答:

798赞 Harley Holcombe 11/26/2008 #1

假设 ASCII 字符串:

string1 = 'Hello'
string2 = 'hello'

if string1.lower() == string2.lower():
    print("The strings are the same (case insensitive)")
else:
    print("The strings are NOT the same (case insensitive)")

从 Python 3.3 开始,casefold() 是一个更好的选择:

string1 = 'Hello'
string2 = 'hello'

if string1.casefold() == string2.casefold():
    print("The strings are the same (case insensitive)")
else:
    print("The strings are NOT the same (case insensitive)")

如果您想要一个更全面的解决方案来处理更复杂的 Unicode 比较,请参阅其他答案。

评论

89赞 tchrist 7/19/2012
这并不总是有效。例如,考虑有两个希腊西格玛,一个只在末尾使用。字符串 Σίσυφος(“Sísyphos”,或更好的“Sísyphos”)具有所有三个:前面的大写字母,末尾的小写字母 final,以及第三个位置的小写非 final。如果你的两个字符串是 和 ,那么你的方法就会失败,因为它们应该是不敏感的相同大小写。ΣίσυφοςΣΊΣΥΦΟΣ
66赞 Harley Holcombe 7/20/2012
@最后两个评论者:我认为假设两个字符串都是 ascii 字符串是公平的。如果你正在寻找一个更令人兴奋的东西的答案,我相信它就在那里(或者你可以问它)。
28赞 kennytm 8/28/2013
问题:为 False。'ß'.lower() == 'SS'.lower()
13赞 Gqqnbig 12/10/2013
希腊字母并不是唯一的特例!在美国英语中,字符“i”(\u0069)是字符“I”(\u0049)的小写版本。然而,土耳其语 (“tr-TR”) 字母表包括一个“带点的 I”字符“İ”(\u0130),它是“i”的大写版本,“I”是“i 不带点”字符“ı”(\u0131) 的大写版本。
34赞 Ethan Reesor 4/28/2016
@HarleyHolcombe假设字符串是 ASCII 如何安全(或公平)呢?该问题没有具体说明,如果字符串在任何时候由用户输入或显示给用户,那么您应该支持国际化。无论如何,新的程序员都会读到这篇文章,我们应该给他们一个真正正确的答案。
4赞 Andru Luvisi 11/26/2008 #2

通常的方法是将字符串大写或小写,以便进行查找和比较。例如:

>>> "hello".upper() == "HELLO".upper()
True
>>> 
2赞 Camilo Díaz Repka 11/26/2008 #3

先转换为小写怎么样?您可以使用 .string.lower()

评论

5赞 tchrist 7/19/2012
你不能比较他们的小写映射:并且不会测试等效的,但应该。ΣίσυφοςΣΊΣΥΦΟΣ
-4赞 Patrick Harrington 11/27/2008 #4
def insenStringCompare(s1, s2):
    """ Method that takes two strings and returns True or False, based
        on if they are equal, regardless of case."""
    try:
        return s1.lower() == s2.lower()
    except AttributeError:
        print "Please only pass strings into this method."
        print "You passed a %s and %s" % (s1.__class__, s2.__class__)

评论

6赞 gerrit 6/13/2017
您正在用打印到 stdout 的消息替换例外,然后返回 None,即 False。这在实践中是非常无益的。
65赞 Nathan Craike 7/20/2012 #5

使用 Python 2,调用每个字符串或 Unicode 对象....lower()

string1.lower() == string2.lower()

...大部分时间都有效,但在@tchrist描述的情况下确实不起作用。

假设我们有一个名为的文件,其中包含两个字符串和 。使用 Python 2:unicode.txtΣίσυφοςΣΊΣΥΦΟΣ

>>> utf8_bytes = open("unicode.txt", 'r').read()
>>> print repr(utf8_bytes)
'\xce\xa3\xce\xaf\xcf\x83\xcf\x85\xcf\x86\xce\xbf\xcf\x82\n\xce\xa3\xce\x8a\xce\xa3\xce\xa5\xce\xa6\xce\x9f\xce\xa3\n'
>>> u = utf8_bytes.decode('utf8')
>>> print u
Σίσυφος
ΣΊΣΥΦΟΣ

>>> first, second = u.splitlines()
>>> print first.lower()
σίσυφος
>>> print second.lower()
σίσυφοσ
>>> first.lower() == second.lower()
False
>>> first.upper() == second.upper()
True

Σ 字符有两种小写形式,ς 和 σ,并且无助于不区分大小写地比较它们。.lower()

但是,从 Python 3 开始,所有三种形式都将解析为 ς,并且在两个字符串上调用 lower() 将正常工作:

>>> s = open('unicode.txt', encoding='utf8').read()
>>> print(s)
Σίσυφος
ΣΊΣΥΦΟΣ

>>> first, second = s.splitlines()
>>> print(first.lower())
σίσυφος
>>> print(second.lower())
σίσυφος
>>> first.lower() == second.lower()
True
>>> first.upper() == second.upper()
True

因此,如果您关心像希腊语中的三西格玛这样的边缘情况,请使用 Python 3。

(作为参考,Python 2.7.3 和 Python 3.3.0b1 显示在上面的解释器打印输出中。

评论

20赞 kgriffs 1/3/2014
为了使比较更加可靠,从 Python 3.3 开始,您可以使用 casefold(例如,first.casefold() == second.casefold())。对于 Python 2,您可以使用 PyICU(另请参阅:icu-project.org/apiref/icu4c/...)
714赞 Veedrac 3/25/2015 #6

以不区分大小写的方式比较字符串似乎微不足道,但事实并非如此。我将使用 Python 3,因为 Python 2 在这里还不发达。

首先要注意的是,Unicode 中去除大小写的转换并非易事。有 的 文本 ,例如 :text.lower() != text.upper().lower()"ß"

>>> "ß".lower()
'ß'
>>> "ß".upper().lower()
'ss'

但是,假设您想无大小写地比较 和 .哎呀,你可能还想比较和相等——这是较新的资本形式。推荐的方法是使用 casefold"BUSSE""Buße""BUSSE""BUẞE"

str.casefold()

返回字符串的折叠副本。折叠的绳子可用于 无大小写匹配。

外壳折叠类似于低壳,但更具侵略性,因为它是 旨在删除字符串中的所有大小写区别。[...]

不要只使用 .如果没有,做会有所帮助(但只是一些)。lowercasefold.upper().lower()

然后你应该考虑口音。如果你的字体渲染器很好,你可能会想 - 但事实并非如此:"ê" == "ê"

>>> "ê" == "ê"
False

这是因为后者的重音是一个组合字符。

>>> import unicodedata
>>> [unicodedata.name(char) for char in "ê"]
['LATIN SMALL LETTER E WITH CIRCUMFLEX']
>>> [unicodedata.name(char) for char in "ê"]
['LATIN SMALL LETTER E', 'COMBINING CIRCUMFLEX ACCENT']

处理此问题的最简单方法是 unicodedata.normalize。您可能想使用 NFKD 规范化,但请随时查看文档。然后一个人做到了

>>> unicodedata.normalize("NFKD", "ê") == unicodedata.normalize("NFKD", "ê")
True

最后,这里用函数表示:

import unicodedata

def normalize_caseless(text):
    return unicodedata.normalize("NFKD", text.casefold())

def caseless_equal(left, right):
    return normalize_caseless(left) == normalize_caseless(right)

评论

16赞 abarnert 5/1/2015
一个更好的解决方案是在摄入时规范化所有字符串,然后你可以只做不区分大小写的比较(更重要的是,区分大小写)。x.casefold() == y.casefold()x == y
7赞 Veedrac 5/1/2015
@abarnert 事实上,根据上下文的不同,有时最好保持源代码不变,但前期规范化也可以使后面的代码更简单。
5赞 abarnert 5/2/2015
@Veedrac:你是对的,这并不总是合适的;如果您需要能够原封不动地输出原始源代码(例如,因为您在 Linux 上处理文件名,其中 NKFC 和 NKFD 都是允许的,并且明确应该不同),显然您不能在输入时转换它......
8赞 4/13/2016
Unicode 标准第 3.13 节还有另外两个用于无大小写比较的定义:(D146,规范)在两侧,(D147,兼容性)在两侧。它指出内部只是为了处理某种希腊口音字符。我想这都是关于边缘情况的。NFD(toCasefold(NFD(str)))NFKD(toCasefold(NFKD(toCasefold(NFD(X)))))NFD
2赞 bortzmeyer 10/3/2017
切诺基字母表很有趣,其中 casefold() 变为大写:>>> “ᏚᎢᎵᎬᎢᎬᏒ”.upper() 'ᏚᎢᎵᎬᎢᎬᏒ' >>> “ᏚᎢᎵkᎬ᎒”.lower() 'ꮪꭲꮅꭼꭲꭼꮢ' >>> “ᏚᎢᎵᎬᎢᎬᏒ”.casefold() 'ᏚᎢᎵᎬᎢᎬᏒ' >>>
9赞 Shiwangi 7/28/2016 #7

我在这里看到了这个使用正则表达式的解决方案。

import re
if re.search('mandy', 'Mandy Pande', re.IGNORECASE):
# is True

它适用于口音

In [42]: if re.search("ê","ê", re.IGNORECASE):
....:        print(1)
....:
1

但是,它不适用于不区分大小写的 unicode 字符。谢谢你@Rhymoid指出,我的理解是它需要确切的符号,才能使案件成立。输出如下:

In [36]: "ß".lower()
Out[36]: 'ß'
In [37]: "ß".upper()
Out[37]: 'SS'
In [38]: "ß".upper().lower()
Out[38]: 'ss'
In [39]: if re.search("ß","ßß", re.IGNORECASE):
....:        print(1)
....:
1
In [40]: if re.search("SS","ßß", re.IGNORECASE):
....:        print(1)
....:
In [41]: if re.search("ß","SS", re.IGNORECASE):
....:        print(1)
....:

评论

7赞 8/19/2016
在不区分大小写的搜索中找不到的事实证明它根本无法处理 Unicode 字符。ßSS
59赞 jfs 11/11/2016 #8

Unicode 标准的第 3.13 节定义了无大小写的算法 匹配。

X.casefold() == Y.casefold()在 Python 3 中实现了“默认无大小写匹配”(D144)。

大小写折叠不会在所有情况下都保留字符串的规范化,因此需要进行规范化 ( vs. )。D145 引入了“规范无大小写匹配”:'å''å'

import unicodedata

def NFD(text):
    return unicodedata.normalize('NFD', text)

def canonical_caseless(text):
    return NFD(NFD(text).casefold())

NFD()对于涉及 U+0345 字符的非常罕见的边缘情况,被调用两次。

例:

>>> 'å'.casefold() == 'å'.casefold()
False
>>> canonical_caseless('å') == canonical_caseless('å')
True

对于(U+3392)和“标识符无大小写匹配”等情况,还有兼容性无大小写匹配(D146),以简化和优化标识符的无大小写匹配'㎒'

评论

6赞 SergiyKolesnikov 12/24/2016
这是 Python 3 的最佳答案,因为 Python 3 使用 Unicode 字符串,并且答案描述了 Unicode 标准如何定义无大小写字符串匹配。
0赞 SergiyKolesnikov 12/24/2016
不幸的是,从 Python 3.6 开始,该函数没有实现大写 I 和点大写 I 的特殊处理,如大小写折叠属性中所述。因此,对于包含这些字母的突厥语单词,比较可能会失败。例如,必须返回 ,但它返回 。目前,在 Python 中处理此问题的唯一方法是编写一个 casefold 包装器或使用外部 Unicode 库,例如 PyICU。casefold()canonical_caseless('LİMANI') == canonical_caseless('limanı')TrueFalse
0赞 jfs 12/24/2016
据我所知,@SergiyKolesnikov.casefold() 的行为应有尽有。从标准中可以看出:“默认的外壳操作旨在在没有针对特定语言和环境进行定制的情况下使用”。土耳其虚线大写字母 I 和无点小 i 的大小写规则在 SpecialCasing.txt 中。“对于非突厥语,通常不使用这种映射。来自 Unicode 常见问题解答: 问:为什么没有额外的字符编码来支持土耳其语与区域设置无关的大小写?
1赞 SergiyKolesnikov 12/24/2016
@j-f-sebastian 我没有说 casefold() 行为不端。如果它实现了一个可选参数,可以对大写字母和点大写字母 I 进行特殊处理,那将是实用的。例如,ICU 库中的 foldCase() 是这样做的:“大小写折叠与语言环境无关,不涉及上下文,但有一个选项可以选择是否包含或排除在 CaseFolding.txt 中标记为”T“的带点 I 和无点 i 的映射。
0赞 Lead Developer 1/19/2021
@jfs 感谢您分享此解决方案。它对我有用。
-4赞 Ali Paul 9/2/2018 #9

这是我上周学会爱/恨的另一个正则表达式,所以通常导入(在这种情况下是,是的)反映我的感受的东西! 做一个正常的功能......请求输入,然后使用 ....something = re.compile(r'foo*|spam*',是的。I)......再。我(是的。我在下面)与 IGNORECASE 相同,但你不能在写它时犯那么多错误!

然后你使用正则表达式搜索你的消息,但老实说,这应该是它自己的几个页面,但关键是 foo 或垃圾邮件被管道连接在一起,大小写被忽略。 然后,如果找到其中任何一个,那么lost_n_found将显示其中一个。如果两者都不是,则 lost_n_found 等于 None。如果它不等于 none,则使用“return lost_n_found.lower()”以小写形式返回user_input

这使您可以更轻松地匹配区分大小写的任何内容。最后,(NCS)代表“没有人认真关心......!”或不区分大小写。无论

如果有人有任何问题,请告诉我。.

    import re as yes

    def bar_or_spam():

        message = raw_input("\nEnter FoO for BaR or SpaM for EgGs (NCS): ") 

        message_in_coconut = yes.compile(r'foo*|spam*',  yes.I)

        lost_n_found = message_in_coconut.search(message).group()

        if lost_n_found != None:
            return lost_n_found.lower()
        else:
            print ("Make tea not love")
            return

    whatz_for_breakfast = bar_or_spam()

    if whatz_for_breakfast == foo:
        print ("BaR")

    elif whatz_for_breakfast == spam:
        print ("EgGs")
0赞 mpriya 3/28/2021 #10

您可以在 str.contains() 中提及 case=False

data['Column_name'].str.contains('abcd', case=False)
10赞 mpriya 3/28/2021 #11

你可以使用 casefold() 方法。casefold() 方法在比较时忽略事例。

firstString = "Hi EVERYONE"
secondString = "Hi everyone"

if firstString.casefold() == secondString.casefold():
    print('The strings are equal.')
else:
    print('The strings are not equal.')

输出:

The strings are equal.
0赞 zackakshay 3/25/2022 #12
def search_specificword(key, stng):
    key = key.lower()
    stng = stng.lower()
    flag_present = False
    if stng.startswith(key+" "):
        flag_present = True
    symb = [',','.']
    for i in symb:
        if stng.find(" "+key+i) != -1:
            flag_present = True
    if key == stng:
        flag_present = True
    if stng.endswith(" "+key):
        flag_present = True
    if stng.find(" "+key+" ") != -1:
        flag_present = True
    print(flag_present)
    return flag_present

输出: search_specificword(“经济适用房”,“欧洲经济适用房的核心”) 假

search_specificword(“经济适用房”,“欧洲经济适用房的核心”) 真

2赞 Jason Leaver 8/21/2022 #13

我找到了一个干净的解决方案,我正在处理一些恒定的文件扩展名。

from pathlib import Path


class CaseInsitiveString(str):
   def __eq__(self, __o: str) -> bool:
      return self.casefold() == __o.casefold()

GZ = CaseInsitiveString(".gz")
ZIP = CaseInsitiveString(".zip")
TAR = CaseInsitiveString(".tar")

path = Path("/tmp/ALL_CAPS.TAR.GZ")

GZ in path.suffixes, ZIP in path.suffixes, TAR in path.suffixes, TAR == ".tAr"

# (True, False, True, True)

评论

1赞 aghast 8/23/2022
谢谢你!这是让 Python“内置”工作的好方法,比如 list.index() 和 “in list” 工作。
0赞 Ryan Leach 11/16/2022
是否需要实现其他任何内容,以使不区分大小写的字符串很好地用作字典键?
0赞 Jason Leaver 11/17/2022
是的,您需要定义方法,在这种情况下,您可能最好使用__hash__class StrEnum(str, Enum):...
0赞 Luciano Narimatsu de Faria 9/22/2022 #14
from re import search, IGNORECASE

def is_string_match(word1, word2):
    #  Case insensitively function that checks if two words are the same
    # word1: string
    # word2: string | list

    # if the word1 is in a list of words
    if isinstance(word2, list):
        for word in word2:
            if search(rf'\b{word1}\b', word, IGNORECASE):
                return True
        return False

    # if the word1 is same as word2
    if search(rf'\b{word1}\b', word2, IGNORECASE):
        return True
    return False

is_match_word = is_string_match("Hello", "hELLO") 
True

is_match_word = is_string_match("Hello", ["Bye", "hELLO", "@vagavela"])
True

is_match_word = is_string_match("Hello", "Bye")
False
0赞 Jason R. Coombs 10/21/2022 #15

考虑使用 jaraco.text 中的 FoldedCase

>>> from jaraco.text import FoldedCase
>>> FoldedCase('Hello World') in ['hello world']
True

如果你想要一个字典,不管大小写都以文本为键,请使用 jaraco.collections 中的 FoldedCaseKeyedDict

>>> from jaraco.collections import FoldedCaseKeyedDict
>>> d = FoldedCaseKeyedDict()
>>> d['heLlo'] = 'world'
>>> list(d.keys()) == ['heLlo']
True
>>> d['hello'] == 'world'
True
>>> 'hello' in d
True
>>> 'HELLO' in d
True