Как сделать сравнение строк без учета регистра?
Как я могу сделать сравнение строк без учета регистра в Python?
Я хотел бы инкапсулировать сравнение регулярных строк со строкой репозитория, используя очень простой и Питонный способ. Я также хотел бы иметь возможность искать значения в dict, хэшированные строками, используя обычные строки python.
11 ответов:
предполагая строки 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, так как Python 2 здесь недостаточно развит.
первое, что нужно отметить, что case-removing преобразования в unicode не являются тривиальными. Есть текст для которого
text.lower() != text.upper().lower(), например"ß":"ß".lower() #>>> 'ß' "ß".upper().lower() #>>> 'ss'но допустим, вы хотели бы случайно сравнить
"BUSSE"и"Buße". Черт, вы, вероятно, также хотите сравнить"BUSSE"и"BUE"равных - это новая форма капитала. Рекомендуется использовать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чтобы закончить, здесь это выражается в функциях:
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, вызывая
.lower()на каждой строке или объекте Unicode...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 показаны в распечатках интерпретатора выше.)
раздел 3.13 стандарта Unicode определяет алгоритмы для caseless совмещение.
X.casefold() == Y.casefold()в Python 3 реализовано "сопоставление без случая по умолчанию" (D144).Casefolding не сохраняет нормализацию строк во всех экземплярах, и поэтому нормализация должна быть выполнена (
'å'и'å'). Д145 вводит "каноническое соответствие безгильзовый":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есть также совместимость caseless matching (D146) для таких случаев, как
'㎒'(U+3392) и" идентификатор caseless matching " для упрощения и оптимизации бескорпусном соответствия идентификаторов.
Я видел это решение здесь используя выражение.
import re if re.search('mandy', 'Mandy Pande', re.IGNORECASE): # is TrueОн хорошо работает с акцентами
In [42]: if re.search("ê","ê", re.IGNORECASE): ....: print(1) ....: 1однако он не работает с символами Юникода без учета регистра. Спасибо @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 >>>
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__)
Это еще одно регулярное выражение, которое я научился любить / ненавидеть за последнюю неделю, поэтому обычно импортируйте как (в этом случае да) что-то, что отражает, как я себя чувствую! сделайте нормальную функцию.... попросите ввода, а затем использовать ....что-то = re.компилировать(r'Foo* / spam*', да.Я.)..... ре.Я (да.Я ниже) - это то же самое, что и IGNORECASE, но вы не можете сделать столько ошибок, написав его!
затем вы ищете свое сообщение с помощью регулярных выражений, но, честно говоря , это должно быть несколько страниц, но дело в том, что foo или спам передаются вместе, и случай игнорируется. Затем, если они будут найдены, то lost_n_found отобразит один из них. если ни тогда lost_n_found равно нет. Если он не равен none, верните user_input в нижнем регистре, используя " return lost_n_found.ниже()"
Это позволяет вам гораздо легче сопоставить все, что будет чувствительно к регистру. Наконец (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")
Если у вас есть список строк, и вы хотите сравнить строки в другой список с учетом регистра. Вот мое решение.
list1 = map(lambda each:each.lower(), list1) list2 = map(lambda each:each.lower(), list2)после этого вы можете сделать сравнение строк ровная.
я использовал это, чтобы сделать что-то более полезно для сравнения двух строк:
def strings_iequal(first, second): try: return first.upper() == second.upper() except AttributeError: if not first: if not second: return Trueобновление: как отмечает Геррит этот ответ имеет некоторые ошибки. Это было много лет назад, и я больше не помню, для чего я его использовал. Я помню, как писал тесты, но что толку от них сейчас!
Comments