如何做一个不区分大小写的string比较?

在Python中执行不区分大小写的string比较的最佳方法是什么?

我想封装一个常规的string比较使用一个非常简单和pythonic方式的储存库string。 我也希望有能力查找由string使用常规的Pythonstring散列的字典中的值。 非常感谢您的build议。

假设ASCIIstring:

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)" 

以不区分大小写的方式比较string看起来像是微不足道的,但事实并非如此。 我将使用Python 3,因为Python 2在这里是欠发达的。

首先要注意的是,在unicode中删除转换的情况并不是微不足道的。 有text.lower() != text.upper().lower()文本,比如"ß"

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

但是让我们假设你想"Buße"比较"BUSSE""Buße" 。 哎呀,你可能还想比较一下"BUSSE""BUẞE" – 这是新的资本forms。 推荐的方法是使用casefold

 help(str.casefold) #>>> Help on method_descriptor: #>>> #>>> casefold(...) #>>> S.casefold() -> str #>>> #>>> Return a version of S suitable for caseless comparisons. #>>> 

不要只使用lower 。 如果casefold不可用,则执行.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 

最后,这里用function来表示:

 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,对每个string或Unicode对象调用.lower()

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

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

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

 >>> 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 

Σ字符有两个小写forms,ς和σ, .lower()不会帮助将它们区分大小写。

但是,从Python 3开始,所有三种forms都将parsing为ς,并且在两个string上调用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 

所以如果你关心像希腊三个sigma这样的边缘情况,可以使用Python 3。

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

Unicode标准的第3.13节定义了用于无情匹配的algorithm。

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

在所有情况下,表情不会保留string的标准化,因此标准化需要完成( 'å''å' )。 D145介绍“规范无匹配”:

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

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

例:

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

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

如何转换为小写字母? 你可以使用string.lower()

我在这里看到这个解决scheme使用正则expression式 。

 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) ....: 

通常的做法是将string大写或小写,以便查找和比较。 例如:

 >>> "hello".upper() == "HELLO".upper() True >>> 
 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__) 

如果列表中包含string,并且您想比较不同列表中的string,而不区分大小写。 这是我的解决scheme。

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

这样做后,你可以很容易地进行string比较。

我用它来完成比较两个string更有用的东西:

 def strings_iequal(first, second): try: return first.upper() == second.upper() except AttributeError: if not first: if not second: return True 

更新 :正如gerrit指出的,这个答案有一些错误。 这是几年前,我不再记得我用它的东西。 我记得写testing,但他们现在有什么好处!