Вопрос-Ответ

How do I do a case-insensitive string comparison?

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

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

Я хотел бы инкапсулировать сравнение обычных строк со строкой репозитория, используя простой код на Pythonic. Я также хотел бы иметь возможность искать значения в dict, хэшированном строками, используя обычные строки 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)")

Начиная с 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)")

Если вам нужно более комплексное решение, которое обрабатывает более сложные сравнения в юникоде, посмотрите Другие ответы.

Ответ 2

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

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

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

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


str.casefold()


Возвращает копию строки с заглавной буквой. Строки с заглавной буквой могут использоваться для сопоставления без заглавной буквы.


Преобразование в регистр символов похоже на использование нижнего регистра, но более агрессивно, поскольку предназначено для удаления всех различий в регистре в строке. [...]


Не используйте просто 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).

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