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

How do I do a case-insensitive string comparison?

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

我想用一种非常简单的方法封装常规字符串与存储库字符串的比较。我还希望能够在使用常规python字符串的字符串散列的dict中查找值。


假设使用ASCII字符串:

1
2
3
4
5
6
7
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,因为这里python 2还未开发。

首先要注意的是,以Unicode格式删除案例转换并不容易。有文本,其中text.lower() != text.upper().lower(),如"?"

1
2
3
4
5
"?".lower()
#>>> '?'

"?".upper().lower()
#>>> 'ss'

但假设你想无案例地比较"BUSSE""Bu?e"。嘿,你可能还想比较一下"BUSSE""Bu?e"相等——这是最新的资本形式。建议使用casefold

1
2
3
4
5
6
7
8
help(str.casefold)
#>>> Help on method_descriptor:
#>>>
#>>> casefold(...)
#>>>     S.casefold() -> str
#>>>    
#>>>     Return a version of S suitable for caseless comparisons.
#>>>

不要只使用lower。如果没有casefold,那么做.upper().lower()会有所帮助(但只是有点帮助)。

那么你应该考虑重音。如果您的字体渲染器很好,您可能认为"ê" =="e?"-但它不:

1
2
"ê" =="e?"
#>>> False

这是因为他们实际上

1
2
3
4
5
6
7
import unicodedata

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

[unicodedata.name(char) for char in"e?"]
#>>> ['LATIN SMALL LETTER E', 'COMBINING CIRCUMFLEX ACCENT']

最简单的处理方法是unicodedata.normalize。您可能希望使用NFKD规范化,但请随意检查文档。然后一个

1
2
unicodedata.normalize("NFKD","ê") == unicodedata.normalize("NFKD","e?")
#>>> True

最后,这里用函数表示:

1
2
3
4
5
6
7
import unicodedata

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

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


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

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

…大部分时间都会工作,但实际上在@tchrist描述的情况下不工作。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> 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
\xce\xa3\xce\x8a\xce\xa3\xce\xa5\xce\xa6\xce\x9f\xce\xa3
'

>>> 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()将正常工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> 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

因此,如果您关心像希腊语中的三个sigma这样的边缘情况,请使用python 3。

(供参考,上面的解释器打印输出中显示了python 2.7.3和python 3.3.0b1。)


Unicode标准第3.13节定义了无案例算法匹配。

python 3中的X.casefold() == Y.casefold()实现了"默认的无大小写匹配"(d144)。

在所有情况下,案例折叠并不能保持字符串的规范化,因此需要进行规范化('?''a?')。D145引入了"规范无案例匹配":

1
2
3
4
5
6
7
import unicodedata

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

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

对于很少出现的涉及U+0345字符的边缘情况,两次调用NFD()

例子:

1
2
3
4
>>> '?'.casefold() == 'a?'.casefold()
False
>>> canonical_caseless('?') == canonical_caseless('a?')
True

对于'?'(u+3392)和"标识符无大小写匹配"等情况,还提供了兼容性无大小写匹配(d146),以简化和优化标识符的无大小写匹配。


我在这里看到了使用regex的解决方案。

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

它和口音很相配

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

但是,它不适用于Unicode字符,不区分大小写。感谢@rhymoid指出,根据我的理解,它需要准确的符号,这样的情况才是正确的。输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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)
....:


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


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

1
2
3
>>>"hello".upper() =="HELLO".upper()
True
>>>

这是另一个regex,我在过去的一周里学会了爱/恨,所以通常导入(在本例中是)一些反映我感觉的东西!做一个正常的功能……请求输入,然后使用….something=re.compile(r'foo*spam*,yes.i)……Re.I(是的,我在下面)和IgnoreCase是一样的,但是你不能犯那么多的错误!

然后使用regex搜索您的消息,但老实说,这应该是它自己的几页,但重点是foo或spam被管道连接在一起,case被忽略。如果找到了其中一个,那么lost-un-found将显示其中一个。如果两者都不存在,则"找到丢失"等于"没有"。如果不等于none,则使用"return lostu found.lower()"返回小写的用户输入。

这使您能够更容易地匹配任何区分大小写的内容。最后一个(ncs)代表"没有人会认真考虑…!"或不区分大小写……以

如果有人有任何问题,请联系我。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    import re as yes

    def bar_or_spam():

        message = raw_input("
Enter 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")

1
2
3
4
5
6
7
8
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__)


我用这个来完成比较两个字符串更有用的事情:

1
2
3
4
5
6
7
def strings_iequal(first, second):
    try:
        return first.upper() == second.upper()
    except AttributeError:
        if not first:
            if not second:
                return True

更新:正如Gerrit所指出的,这个答案有一些错误。这是多年前的事了,我不再记得我用它做什么了。我的确记得写测试,但它们现在有什么用!


如果您有带字符串的列表,并且希望将不同列表中的字符串与不区分大小写的字符串进行比较。这是我的解决方案。

1
2
list1 = map(lambda each:each.lower(), list1)
list2 = map(lambda each:each.lower(), list2)

这样做之后,您可以轻松地进行字符串比较。