Как выполнить сравнение строк без учета регистра?

Как в Python выполнить сравнение строк без учета регистра?

Я хотел бы инкапсулировать сравнение обычных строк со строкой репозитория, используя очень простой и питонический способ. Я также хотел бы иметь возможность искать значения в dict, хешированном по строкам, с использованием обычных строк Python.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
623
0
834 350
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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)")

Это не всегда работает. Для примера представьте, что есть две греческие сигмы, одна из которых используется только в конце. Строка Σίσυφος («Sísyphos», или лучше «Síſyphos») имеет все три: верхний регистр спереди, нижний регистр в конце и нижний нефинал в третьей позиции. Если у вас две строки - Σίσυφος и ΣΊΣΥΦΟΣ, то ваш подход не работает, потому что они должны быть одинаковыми без учета регистра.

tchrist 19.07.2012 17:42

@ Последние два комментатора: я думаю, будет справедливо предположить, что обе строки являются строками ascii. Если вы ищете ответ на что-то более захватывающее, я уверен, что он есть (или вы можете спросить об этом).

Harley Holcombe 20.07.2012 05:34

Подход .lower () будет работать в Python 3, по крайней мере, для двух упомянутых выше греческих строк. См. Мой ответ для более подробной информации.

Nathan Craike 20.07.2012 09:28

@tchrist Есть ли примеры, когда подход .lower () не работает в Python 3? Приведенный вами пример с греческим языком, похоже, отлично работает в Python 3. Кроме того, было бы здорово, если бы вы могли опубликовать решение, которое правильно обрабатывает эти крайние случаи, даже если оно использует сторонний модуль, такой как pyICU.

Nathan Craike 24.07.2012 14:50

Проблема: 'ß'.lower() == 'SS'.lower() неверен.

kennytm 28.08.2013 18:10

@KennyTM, почему это может быть проблемой? en.wikipedia.org/wiki/Capital_%E1%BA%9E вроде опускается правильно.

exic 05.12.2013 20:00

Греческие буквы - не единственный особый случай! В английском языке США символ "i" (\ u0069) - это строчная версия символа "I" (\ u0049). Однако турецкий («tr-TR») алфавит включает в себя символ «I с точкой» «İ» (\ u0130), который является заглавной версией «i», а «I» - заглавной версией «i без точка "ı" (\ u0131).

Gqqnbig 10.12.2013 06:08

@exic, эта статья в Википедии довольно ясно показывает, что, по мнению большинства немцев, «Capital Eszett» - не настоящее письмо. Он закодирован в Unicode, так что есть представление для некоторых типографских курьезов, но это не имеет отношения к точке зрения Кенни. (То есть вы утверждаете, что немецкий и турецкий языки должны изменить свои системы письма, чтобы лучше работать с семантикой Python, но чаще всего утверждают обратное: Python должен найти способ работать с немецкими и турецкими системами письма, поскольку они используются настоящие немецкие и турецкие люди.)

Quuxplusone 31.03.2014 22:56

@HarleyHolcombe, как безопасно (или справедливо) предполагать, что строки являются ascii? В вопросе не указывалось, и если строки в какой-то момент вводятся пользователем или показываются пользователю, то вы должны поддерживать интернационализацию. Тем не менее, новые программисты будут читать это, и мы должны дать им действительно правильный ответ.

Ethan Reesor 27.04.2016 21:28

Для комментаторов выше это нормально. Работает нормально. Если вы хотите передать неанглийские латинские языки, греческие языки, кириллицу, армянские языки или странные символы, см. Ответ @Veedrac.

user3932000 07.07.2016 13:54

@ user3932000 Другими словами, этот ответ хорош только тогда, когда вы имеете дело с текстом, который действительно является исключительно английским. Для большинства людей, а именно людей, чей родной язык не английский, людей, которым приходится иметь дело с проблемами l10n / i18n, и людей, которым приходится иметь дело с очисткой ввода Unicode, это означает, что этот ответ - неправильный.

user824425 19.08.2016 15:31

@ Рифмоид да. Это не работает даже для "исключительно английского" текста, например, "fish".casefold() == "Fish".casefold() работает, а .lower() здесь не работает. Хотя могут быть случаи даже .casefold() не хватает

jfs 12.11.2016 00:24

@ user3932000 Тогда ответ бессмысленен в любом профессиональном контексте. Это нет - правильный способ сравнения строк без учета регистра. Это обходной путь, который не работает в некоторых конкретных случаях.

Basic 02.12.2016 20:28

@Basic Вы правы, что в профессиональном контексте это бессмысленно. Ни одному клиенту не понадобится алгоритм, который так легко сломается из-за нестандартного или неанглийского ввода. Но для личных целей этот алгоритм практичен и отлично подходит.

user3932000 02.12.2016 22:15

@Quuxplusone, как и следовало ожидать, в июне 2017 года ẞ стала частью официальной немецкой орфографии. Однако алгоритм вопроса остается неверным, например из-за турецкого примера и прочего.

flying sheep 22.05.2018 14:57

@HarleyHolcombe Замена .lower() на .casefold() настолько проста и позволяет избежать многих проблем (не всех), что мы должны научить новых программистов использовать значительно улучшенную версию и избегать обучения, которое работает только с частью глобальных пользователей.

Marcel Waldvogel 11.11.2020 18:26

Обычный подход - вводить строки в верхний регистр или в нижний регистр для поиска и сравнения. Например:

>>> "hello".upper() == "HELLO".upper()
True
>>> 

Как насчет того, чтобы сначала преобразовать в нижний регистр? можно использовать string.lower().

Вы не можете сравнивать их строчные карты: Σίσυφος и ΣΊΣΥΦΟΣ не будут тестировать эквивалент, но должны.

tchrist 19.07.2012 18:27
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__)

Вы заменяете исключение сообщением, выводимым на стандартный вывод, а затем возвращаете None, что равно False. На практике это очень бесполезно.

gerrit 12.06.2017 21:10

Используя 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 показаны на распечатках интерпретатора выше.)

Чтобы сделать сравнение еще более надежным, начиная с Python 3.3 вы можете использовать casefold (например, first.casefold () == second.casefold ()). Для Python 2 вы можете использовать PyICU (см. Также: icu-project.org/apiref/icu4c/…)

kgriffs 02.01.2014 20:38

Сравнение строк нечувствительным к регистру способом кажется тривиальным, но это не так. Я буду использовать Python 3, поскольку Python 2 здесь недостаточно развит.

Прежде всего следует отметить, что преобразования с удалением регистра в Юникоде нетривиальны. Есть текст, для которого text.lower() != text.upper().lower(), например "ß":

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

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

Но допустим, вы хотели без футляра сравнить "BUSSE" и "Buße". Черт возьми, вы, вероятно, также захотите сравнить "BUSSE" и "BUẞE" в равной степени - это новая форма заглавной буквы. Рекомендуемый способ - использовать casefold:

str.casefold()

Return a casefolded copy of the string. Casefolded strings may be used for caseless matching.

Casefolding is similar to lowercasing but more aggressive because it is intended to remove all case distinctions in a string. [...]

Не используйте просто 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 для чувствительности к регистру).

abarnert 01.05.2015 13:44

@abarnert Действительно, в зависимости от контекста - иногда лучше оставить исходный код нетронутым, но предварительная нормализация также может значительно упростить последующий код.

Veedrac 01.05.2015 15:13

@Veedrac: Вы правы, это не всегда уместно; если вам нужно иметь возможность выводить исходный источник без изменений (например, потому что вы имеете дело с именами файлов в Linux, где NKFC и NKFD разрешены и явно должны быть разными), очевидно, вы не можете преобразовать его на входе ...

abarnert 02.05.2015 01:14

В разделе 3.13 стандарта Unicode есть два других определения для сравнения без регистра: (D146, канонический) NFD(toCasefold(NFD(str))) с обеих сторон и (D147, совместимость) NFKD(toCasefold(NFKD(toCasefold(NFD(X))))) с обеих сторон. В нем говорится, что внутренний NFD предназначен исключительно для обработки определенного символа греческого акцента. Думаю, все дело в крайних случаях.

user2379410 12.04.2016 20:06

И немного забавы с алфавитом чероки, где casefold () переходит в верхний регистр: >>> "ᏚᎢᎵᎬᎢᎬᏒ". Upper () 'ᏚᎢᎵᎬᎢᎬᏒ' >>> "ᏚᎢᎵᎬᎢᎬᏒ". Lower () 'ꮪꭲꮅꭼꭲꭼꮢ' >>> "ᏚᎢᎵᎬᎢᎬᏒ" .casefold () 'ᏚᎢᎵᎬᎢᎬᏒ' >>>

bortzmeyer 02.10.2017 21:41

Если вы используете Python 2, вы можете проверить py2casefold, чтобы получить недостающую функциональность casefold.

kuzzooroo 26.11.2017 19:51

нет, я бы точно не хотел, чтобы «Буссе» и «Буссе» считались равными. Это два разных слова с разным произношением и совершенно разным значением.

jakun 11.10.2019 09:34

@jakun «СС» - это традиционная заглавная буква «ß»., значит, «BUSSE» - это заглавная буква «buße», не так ли? Следовательно, «BUSSE» без учета регистра должно быть равно «buße».

Veedrac 11.10.2019 19:58

@Veedrac правда. Я думаю, что мой комментарий был слишком резким, извините за это. Я считаю, что заглавная еще не так широко известна, и Duden все еще разрешает SS вместо этого. Поэтому «BUSSE» действительно неоднозначен. Тот факт, что я сначала прочитал «BUSSE» как заглавную форму «Busse», объясняется не только устареванием заглавных букв, но, вероятно, большей частью потому, что «Buße» встречается реже, чем «Busse». Тем не менее, "Busse".casefold() == "Buße".casefold() возвращает True, и это имхо неверно.

jakun 14.10.2019 16:46

Я видел это решение здесь с использованием регулярное выражение.

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

Тот факт, что ß не найден в SS при поиске без учета регистра, свидетельствует о том, что он не работает работает с символами Unicode совсем.

user824425 19.08.2016 15:34

Раздел 3.13 стандарта Unicode определяет алгоритмы без дела соответствие.

X.casefold() == Y.casefold() в Python 3 реализует «сопоставление без регистра по умолчанию» (D144).

Casefolding не сохраняет нормализацию строк во всех случаях, и поэтому необходимо выполнить нормализацию ('å' против 'å'). 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) и «сопоставление без регистра идентификатора» для упрощения и оптимизации сопоставление идентификаторов без регистра.

Это лучший ответ для Python 3, потому что Python 3 использует строки Unicode, и ответ описывает, как стандарт Unicode определяет сопоставление строк без регистра.

SergiyKolesnikov 23.12.2016 20:23

К сожалению, начиная с Python 3.6, функция casefold() не реализует особую обработку прописных букв I и прописных букв I с точками, как описано в Свойства складывания корпуса. Следовательно, сравнение может быть неудачным для слов из тюркских языков, содержащих эти буквы. Например, canonical_caseless('LİMANI') == canonical_caseless('limanı') должен возвращать True, но возвращает False. В настоящее время единственный способ справиться с этим в Python - написать оболочку casefold или использовать внешнюю библиотеку Unicode, такую ​​как PyICU.

SergiyKolesnikov 23.12.2016 21:17

@SergiyKolesnikov .casefold (), насколько я могу судить, ведет себя должным образом. Из стандарта: "операции с регистром по умолчанию предназначены для использования в отсутствие адаптации для определенных языков и сред". Правила регистра для турецкой заглавной буквы I и строчной буквы без точки i находятся в SpecialCasing.txt. «Для нетюркских языков это сопоставление обычно не используется». Из FAQ по Unicode: В: Почему нет дополнительных символов, закодированных для поддержки регистра для турецкого языка, не зависящего от локали?

jfs 23.12.2016 23:13

@ j-f-sebastian Я не говорил, что casefold () плохо себя ведет. Было бы практично, если бы он реализовал необязательный параметр, который включал специальную обработку прописных букв I, разделенных точками. Например, способ foldCase () в библиотеке ICU делает это: «Сворачивание регистра не зависит от языка и контекста, но есть опция для следует ли включать или исключать сопоставления для точек I и i без точек, которые помечены буквой 'T' в CaseFolding.txt ".

SergiyKolesnikov 24.12.2016 01:02

@jfs Спасибо, что поделились этим решением. У меня это сработало.

Lead Developer 19.01.2021 10:59

Это еще одно регулярное выражение, которое я научился любить / ненавидеть за последнюю неделю, поэтому обычно импортирую как (в данном случае да) что-то, что отражает мои чувства! сделать нормальную функцию .... запросить ввод, затем использовать .... something = re.compile (r'foo * | spam * ', yes.I) ...... re.I (yes.I ниже) - то же самое, что IGNORECASE, но вы не можете сделать столько ошибок, написав его!

Затем вы выполняете поиск своего сообщения, используя регулярные выражения, но, честно говоря, это должно быть несколько страниц, но дело в том, что foo или спам передаются вместе, а регистр игнорируется. Затем, если они найдены, lost_n_found отобразит один из них. если нет, то lost_n_found равно None. Если не равно none, верните user_input в нижнем регистре, используя "return lost_n_found.lower ()"

Это позволяет вам гораздо легче сопоставить все, что будет чувствительно к регистру. Наконец, (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")

Вы можете упомянуть case = False в str.contains ()

data['Column_name'].str.contains('abcd', case=False)

Вы можете использовать метод 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.

Другие вопросы по теме