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

Как я могу провести сравнение строк в Python?

Я хотел бы инкапсулировать сравнение регулярных строк в строку репозитория, используя очень простой и питонический путь. Я также хотел бы иметь возможность искать значения в dict hashed с помощью строк, использующих обычные строки python.

Ответ 1

Предполагая строки 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)"

Ответ 2

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

В первую очередь следует отметить, что конверсии case-remove в unicode не являются тривиальными. Существует текст, для которого text.lower() != text.upper().lower(), например "ß":

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

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

Но скажем, вы хотели без лишних сравнений "BUSSE" и "Buße". Черт, вы, вероятно, также хотите сравнить "BUSSE" и "BUẞE" equal - форму новой формы капитала. Рекомендуемый способ - использовать 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)

Ответ 3

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

Ответ 4

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

X.casefold() == Y.casefold() в Python 3 реализует "стандартное несоответствие без содержания" (D144).

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

Ответ 5

Я видел это решение здесь, используя regex.

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

Ответ 6

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

Ответ 7

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

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

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

Ответ 9

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

Затем вы ищите свое сообщение с помощью регулярных выражений, но, честно говоря, это должно быть несколько отдельных страниц, но суть в том, что foo или spam передаются вместе, а case игнорируется. Затем, если любой из них найден, lost_n_found отобразит один из них. если ни то, ни другое lost_n_found равно 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")

Ответ 10

Если у вас есть списки со строками, и вы хотите сравнить строки в другом списке с регистром без учета. Вот мое решение.

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

После этого вы можете легко выполнить сравнение строк.

Ответ 11

Я использовал это, чтобы выполнить что-то более полезное для сравнения двух строк:

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

Обновление: как отмечено gerrit, этот ответ содержит некоторые ошибки. Это было много лет назад, и я больше не помню, для чего я его использовал. Я действительно помню, как писал тесты, но насколько они хороши!