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格式删除案例转换并不容易。有文本,其中
1 2 3 4 5 | "?".lower() #>>> '?' "?".upper().lower() #>>> 'ss' |
但假设你想无案例地比较
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. #>>> |
不要只使用
那么你应该考虑重音。如果您的字体渲染器很好,您可能认为
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'] |
最简单的处理方法是
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对象调用
1 | string1.lower() == string2.lower() |
…大部分时间都会工作,但实际上在@tchrist描述的情况下不工作。
假设我们有一个名为
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 |
∑字符有两个小写形式,?而σ和
但是,从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中的
在所有情况下,案例折叠并不能保持字符串的规范化,因此需要进行规范化(
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字符的边缘情况,两次调用
例子:
1 2 3 4 | >>> '?'.casefold() == 'a?'.casefold() False >>> canonical_caseless('?') == canonical_caseless('a?') True |
对于
我在这里看到了使用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) ....: |
先转换成小写怎么样?您可以使用
通常的方法是将字符串大写或小写以进行查找和比较。例如:
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) |
这样做之后,您可以轻松地进行字符串比较。