如何在Python中以不区分大小写的方式比较字符串?
我希望使用简单且符合Python习惯的代码封装普通字符串与存储库字符串之间的比较。我还希望能够使用普通Python字符串在由字符串哈希的字典中查找值。
如何在Python中以不区分大小写的方式比较字符串?
我希望使用简单且符合Python习惯的代码封装普通字符串与存储库字符串之间的比较。我还希望能够使用普通Python字符串在由字符串哈希的字典中查找值。
假设字符串是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比较问题,想要一个更全面的解决方案,请参阅其他答案。
在不区分大小写的情况下比较字符串似乎很简单,但实际上并非如此。我将使用Python 3,因为Python 2在这方面不够发达。
首先要注意的是,在Unicode中进行大小写转换并不是一件轻松的事情。有些文本的 text.lower() != text.upper().lower()
,例如"ß"
:
>>> "ß".lower()
'ß'
>>> "ß".upper().lower()
'ss'
假设你想要忽略大小写地比较 "BUSSE"
和 "Buße"
。实际上,你可能还想要将 "BUSSE"
和新的大写形式 "BUẞE"
视为相等。建议的方法是使用casefold
:
str.casefold()
返回字符串的 casefolded 副本。Casefolded 字符串可用于无大小写区分的匹配。
casefold 与 lowercasing 类似,但更为激进,因为它旨在消除字符串中所有的大小写区分。[...]
不要仅仅使用 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
为了结束,这里将它表述为函数:
import unicodedata
def normalize_caseless(text):
return unicodedata.normalize("NFKD", text.casefold())
def caseless_equal(left, right):
return normalize_caseless(left) == normalize_caseless(right)
x.casefold() == y.casefold()
进行不区分大小写的比较(更重要的是,使用 x == y
进行区分大小写的比较)。 - abarnertNFD(toCasefold(NFD(str)))
和(D147,兼容性)在两侧使用NFKD(toCasefold(NFKD(toCasefold(NFD(X)))))
。它指出内部的NFD
仅用于处理特定的希腊重音字符。我猜这都是关于边缘情况的。 - user2379410- bortzmeyer"ᏚᎢᎵᎬᎢᎬᏒ".upper() 'ᏚᎢᎵᎬᎢᎬᏒ' "ᏚᎢᎵᎬᎢᎬᏒ".lower() 'ꮪꭲꮅꭼꭲꭼꮢ' "ᏚᎢᎵᎬᎢᎬᏒ".casefold() 'ᏚᎢᎵᎬᎢᎬᏒ'
在Python 2中,对每个字符串或Unicode对象调用 .lower()
...
string1.lower() == string2.lower()
......将在大多数情况下有效,但在@tchrist描述的情况下确实无法正常工作。
假设我们有一个名为unicode.txt
的文件,其中包含两个字符串Σίσυφος
和ΣΊΣΥΦΟΣ
。使用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
Σ字符有两种小写形式,分别是ς和σ,而.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。)
Unicode 标准第 3.13 节 定义了无大小写匹配的算法。
Python 3 中 X.casefold() == Y.casefold()
实现了“默认无大小写匹配”(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
还有兼容性不区分大小写匹配(D146),用于像'㎒'
(U + 3392)这样的情况,以及“标识符不区分大小写匹配”,以简化和优化标识符的不区分大小写匹配。(Unicode技术报告#31)
casefold()
函数未实现大写字母I和带点的大写字母I的特殊情况处理,如大小写折叠属性所述。因此,对于包含这些字母的突厥语言单词,比较可能会失败。例如,canonical_caseless('LİMANI') == canonical_caseless('limanı')
应返回True
,但实际上返回了False
。目前,在Python中处理这个问题的唯一方法是编写一个casefold包装器或使用外部Unicode库,例如PyICU。 - SergiyKolesnikov您可以使用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.
我在这里看到了一个解决方案,使用正则表达式来匹配字符串中的子串,忽略大小写。具体解决方案请参考此处。
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)
....:
>>> "hello".upper() == "HELLO".upper()
True
>>>
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)
__hash__
方法,这种情况下最好使用class StrEnum(str, Enum):...
。 - Jason Leaver首先,您可以使用string.lower()
将文本转换为小写字母。
Σίσυφος
和ΣΊΣΥΦΟΣ
虽然应该测试结果相同,但实际上并不相同。 - tchristdef 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("经济适用房", "到欧洲经济适用房的核心") False
search_specificword("经济适用房", "到欧洲经济适用房的核心") True
Σίσυφος
和ΣΊΣΥΦΟΣ
,那么你的方法会失败,因为它们应该是大小写不敏感的相同单词。 - tchrist'ß'.lower() == 'SS'.lower()
为假。 - kennytm