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

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

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

вопрос задан 26.11.2008
Kozyarchuk
9450 репутация

11 ответов


  • 434 рейтинг

    Предполагая строки 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)"
    
    ответ дан Harley Holcombe, с репутацией 105065, 26.11.2008
  • 353 рейтинг

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

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

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

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

    .
    help(str.casefold)
    #>>> Help on method_descriptor:
    #>>>
    #>>> casefold(...)
    #>>>     S.casefold() -> str
    #>>>     
    #>>>     Return a version of S suitable for caseless comparisons.
    #>>>
    

    Не просто используйте lower. Если casefold недоступен, 363638674 помогает (но только в некоторой степени).

    Тогда вы должны рассмотреть акценты. Если ваш рендерер шрифтов хорош, вы, вероятно, думаете "ê" == "ê" - но это не так:

    "ê" == "ê"
    #>>> 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)
    
    ответ дан Veedrac, с репутацией 38011, 25.03.2015
  • 50 рейтинг

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

    ответ дан Nathan Craike, с репутацией 2886, 20.07.2012
  • 16 рейтинг

    Раздел 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) и «сопоставление без учета идентификатора», для упрощения и оптимизации сопоставления без учета идентификаторов .

    ответ дан jfs, с репутацией 252362, 11.11.2016
  • 4 рейтинг

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

    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)
    ....:
    
    ответ дан Shiwangi, с репутацией 84, 28.07.2016
  • 3 рейтинг

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

    ответ дан Camilo Díaz Repka, с репутацией 3236, 26.11.2008
  • 2 рейтинг

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

    >>> "hello".upper() == "HELLO".upper()
    True
    >>> 
    
    ответ дан Glomek, с репутацией 18875, 26.11.2008
  • 0 рейтинг

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

    Затем вы ищите свое сообщение с помощью регулярных выражений, но, честно говоря, это должно быть несколько страниц само по себе, но дело в том, что foo или spam передаются вместе, а регистр игнорируется. Затем, если любой из них найден, 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")
    
    ответ дан Ali Paul, с репутацией 1, 2.09.2018
  • -1 рейтинг
    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__)
    
    ответ дан Patrick Harrington, с репутацией 27535, 26.11.2008
  • -8 рейтинг

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

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

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

    ответ дан Chris, с репутацией 87, 23.12.2014
  • -8 рейтинг

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

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

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

    ответ дан caesar, с репутацией 749, 24.08.2013