Игнорировать регистр в строках Python [дубликат]

этот вопрос уже есть ответ здесь:

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

каков самый простой способ сравнить строки в Python, игнорируя регистр?

конечно, можно сделать (str1.lower ()

Я думаю,что ищу эквивалент stricmp () C.

[еще один запрошенный контекст, поэтому я продемонстрирую с тривиальным примером:]

Предположим, вы хотите отсортировать список строк looong. Вы просто делаете список.род.)( Это o(n * log (n)) сравнение строк и отсутствие управления памятью (так как все строки и элементы списка - это своего рода интеллектуальные указатели). Вы счастливы.

теперь вы хотите сделать то же самое, но игнорируйте случай (давайте упростим и скажем все строки являются ascii, поэтому проблемы локали можно игнорировать). Вы можете сделать список.сортировка (ключ=лямбда s: s.ниже ()), но затем вы вызываете два новых распределения на сравнение, плюс нагрузка мусоросборника с дублированным (опускает) струны. Каждый такой шум управления памятью на порядок медленнее, чем простое сравнение строк.

теперь, с функцией stricmp () на месте, вы делаете: theList.сортировать (cmp=stricmp) и это так же быстро и память-это список.род.)( Вы снова счастливы.

проблема заключается в том, что любое сравнение на основе Python без учета регистра включает неявную строку дублирование, поэтому я ожидал найти сравнение на основе C (возможно, в строке модуля).

Не могу найти ничего подобного, поэтому вопрос здесь. (Надеюсь, это проясняет вопрос).

16 ответов


в ответ на Ваше разъяснение...

можно использовать ctypes для выполнения функции c "strcasecmp". Ctypes входит в Python 2.5. Он предоставляет возможность вызывать dll и общие библиотеки, такие как libc. Вот краткий пример (Python в Linux; см. ссылку для справки Win32):

from ctypes import *
libc = CDLL("libc.so.6")  // see link above for Win32 help
libc.strcasecmp("THIS", "this") // returns 0
libc.strcasecmp("THIS", "THAT") // returns 8

может также хотеть ссылаться документация strcasecmp

Не уверен, что это быстрее или медленнее (не тестировалось), но это способ использовать функцию C для сравнения строк без учета регистра.

~~~~~~~~~~~~~~

Код ActiveState-Рецепт 194371: Строки Без Учета Регистра является рецептом для создания класса String без учета регистра. Это может быть немного больше kill для чего-то быстрого, но может предоставить вам общий способ обработки нечувствительных к регистру строк, Если вы планируете использовать их часто.


вот тест, показывающий, что использование str.lower быстрее, чем предлагаемый метод принятого ответа (libc.strcasecmp):

#!/usr/bin/env python2.7
import random
import timeit

from ctypes import *
libc = CDLL('libc.dylib') # change to 'libc.so.6' on linux

with open('/usr/share/dict/words', 'r') as wordlist:
    words = wordlist.read().splitlines()
random.shuffle(words)
print '%i words in list' % len(words)

setup = 'from __main__ import words, libc; gc.enable()'
stmts = [
    ('simple sort', 'sorted(words)'),
    ('sort with key=str.lower', 'sorted(words, key=str.lower)'),
    ('sort with cmp=libc.strcasecmp', 'sorted(words, cmp=libc.strcasecmp)'),
]

for (comment, stmt) in stmts:
    t = timeit.Timer(stmt=stmt, setup=setup)
    print '%s: %.2f msec/pass' % (comment, (1000*t.timeit(10)/10))

типичные времена на моей машине:

235886 words in list
simple sort: 483.59 msec/pass
sort with key=str.lower: 1064.70 msec/pass
sort with cmp=libc.strcasecmp: 5487.86 msec/pass

Итак, версия с str.lower - это не только самый быстрый, но и самый портативный и обновления всех предложенных решений здесь. Я не профилировал использование памяти, но оригинальный плакат все еще не дал убедительных причин беспокоиться об этом. Также, кто говорит, что вызов в модуль libc не дублирует какие-либо строки?

NB:lower() строковый метод также имеет преимущество быть зависимым от локали. Что-то, что вы, вероятно, не получите правильно при написании собственного "оптимизированного" решения. Тем не менее, из-за ошибок и отсутствующих функций в Python такое сравнение может дать неправильные результаты в контексте unicode.


ваш вопрос подразумевает, что вам не нужен Unicode. Попробуйте следующий фрагмент кода, если он работает для вас, вы сделали:

Python 2.5.2 (r252:60911, Aug 22 2008, 02:34:17)
[GCC 4.3.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import locale
>>> locale.setlocale(locale.LC_COLLATE, "en_US")
'en_US'
>>> sorted("ABCabc", key=locale.strxfrm)
['a', 'A', 'b', 'B', 'c', 'C']
>>> sorted("ABCabc", cmp=locale.strcoll)
['a', 'A', 'b', 'B', 'c', 'C']

пояснение: в случае, если это не очевидно на первый взгляд, язык.strcoll кажется функцией, которая вам нужна, избегая str.ниже или ниже.strxfrm "дублирует" строки.


используете ли вы это сравнение в очень часто выполняемом пути приложения с высокой производительностью? Кроме того, вы используете это для строк размером в мегабайты? Если нет, то вы не должны беспокоиться о производительности и просто использовать .ниже() метод.

следующий код демонстрирует, что выполнение сравнения без учета регистра путем вызова .lower() на двух строках, каждая из которых имеет размер почти мегабайт, занимает около 0,009 секунды на моем рабочем столе 1.8 GHz компьютер:

from timeit import Timer

s1 = "1234567890" * 100000 + "a"
s2 = "1234567890" * 100000 + "B"

code = "s1.lower() < s2.lower()"
time = Timer(code, "from __main__ import s1, s2").timeit(1000)
print time / 1000   # 0.00920499992371 on my machine

Если это действительно чрезвычайно важный, критичный для производительности раздел кода, то я рекомендую написать функцию на C и вызвать ее из вашего кода Python, так как это позволит вам выполнить действительно эффективный поиск без учета регистра. Подробнее о написании модулей расширения C можно найти здесь:https://docs.python.org/extending/extending.html


Я не могу найти другой встроенный способ выполнения сравнения без учета регистра: рецепт кулинарной книги python использует lower ().

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


нет встроенный эквивалентную функцию.

вы можете написать свою функцию, которая преобразует .lower () каждый символ за раз, чтобы избежать дублирования обеих строк, но я уверен, что он будет очень интенсивным и чрезвычайно неэффективным.

Если вы не работаете с очень длинными строками (так долго, что может вызвать проблему с памятью, если дублируется), то я бы оставил его простым и использовать

str1.lower() == str2.lower()

все будет хорошо


этот вопрос задает 2 очень разные вещи:

  1. каков самый простой способ сравнить строки в Python, игнорируя регистр?
  2. Я думаю, что ищу эквивалент stricmp () C.

поскольку #1 уже получил очень хороший ответ (т. е.: str1.lower ()

def strincmp(str1, str2, numchars=None):
    result = 0
    len1 = len(str1)
    len2 = len(str2)
    if numchars is not None:
        minlen = min(len1,len2,numchars)
    else:
        minlen = min(len1,len2)
    #end if
    orda = ord('a')
    ordz = ord('z')

    i = 0
    while i < minlen and 0 == result:
        ord1 = ord(str1[i])
        ord2 = ord(str2[i])
        if ord1 >= orda and ord1 <= ordz:
            ord1 = ord1-32
        #end if
        if ord2 >= orda and ord2 <= ordz:
            ord2 = ord2-32
        #end if
        result = cmp(ord1, ord2)
        i += 1
    #end while

    if 0 == result and minlen != numchars:
        if len1 < len2:
            result = -1
        elif len2 < len1:
            result = 1
        #end if
    #end if

    return result
#end def

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

Я работаю только со строками ascii, я не уверен, как это будет вести себя с unicode.


когда что-то не поддерживается хорошо в стандартной библиотеке, я всегда ищу пакет PyPI. С виртуализацией и повсеместностью современных дистрибутивов Linux я больше не избегаю расширений Python. PyICU, кажется, подходит для счета:https://stackoverflow.com/a/1098160/3461

теперь есть также опция, которая является чистым python. Это хорошо проверено:https://github.com/jtauber/pyuca


старый ответ:

Мне нравится решение регулярного выражения. Вот функция, которую вы можете скопировать и вставить в любую функцию, благодаря поддержке блочной структуры python.

def equals_ignore_case(str1, str2):
    import re
    return re.match(re.escape(str1) + r'\Z', str2, re.I) is not None

поскольку я использовал match вместо search, мне не нужно было добавлять курсор ( ^ ) в регулярное выражение.

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


вот как вы это сделаете с re:

import re
p = re.compile('^hello$', re.I)
p.match('Hello')
p.match('hello')
p.match('HELLO')

рекомендуемая идиома для сортировки списков значений с использованием дорогостоящих для вычисления ключей относится к так называемому "оформленному шаблону". Он состоит просто в создании списка (ключа, значения) кортежей из исходного списка и сортировке этого списка. Тогда тривиально исключить ключи и получить список отсортированных значений:

>>> original_list = ['a', 'b', 'A', 'B']
>>> decorated = [(s.lower(), s) for s in original_list]
>>> decorated.sort()
>>> sorted_list = [s[1] for s in decorated]
>>> sorted_list
['A', 'a', 'B', 'b']

или если вы любите ОДН-вкладыши:

>>> sorted_list = [s[1] for s in sorted((s.lower(), s) for s in original_list)]
>>> sorted_list
['A', 'a', 'B', 'b']

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


Я уверен, что вы либо должны использовать .lower () или используйте регулярное выражение. Я не знаю встроенной функции сравнения строк без учета регистра.


для случайных или даже повторяющихся сравнений несколько дополнительных строковых объектов не должны иметь значения, пока это не произойдет в самом внутреннем цикле вашего основного кода или у вас недостаточно данных, чтобы фактически заметить влияние производительности. Смотрите, если вы делаете: делать вещи "глупым" способом намного менее глупо, если вы также делаете это меньше.

Если вы серьезно хотите продолжать сравнивать много и много текста без учета регистра, вы можете каким-то образом сохранить строчные версии строк под рукой чтобы избежать завершения и повторного создания или нормализации всего набора данных в нижнем регистре. Это, конечно, зависит от размера набора данных. Если есть относительно мало игл и большой стог сена, замена игл скомпилированными объектами regexp является одним из решений. Трудно сказать, не видя конкретного примера.


вы можете перевести каждую строку в нижний регистр один раз - - - лениво только тогда, когда вам это нужно, или в качестве предварительной подготовки к сортировке, если вы знаете, что будете сортировать всю коллекцию строк. Существует несколько способов прикрепить этот ключ сравнения к фактическим сортируемым данным, но эти методы следует рассмотреть в отдельном вопросе.

обратите внимание, что этот метод может использоваться не только для обработки верхнего / нижнего регистра, но и для других типов сортировки, таких как сортировка по локали, или сортировка заголовков в стиле библиотеки, которая игнорирует ведущие статьи и нормализует данные перед сортировкой.


просто использовать str().lower() метод, если не важна высокая производительность - в этом случае напишите этот метод сортировки как расширение C.

"Как написать расширение Python" кажется приличным вступлением..

более интересно, данное руководство сравнивает использование библиотеки ctypes против написания внешнего модуля C (ctype довольно-существенно медленнее, чем расширение C).


import re
if re.match('tEXT', 'text', re.IGNORECASE):
    # is True

вы могли бы подкласс str и создайте свой собственный нечувствительный к регистру класс строк, но ИМХО, что было бы крайне неразумно и создать гораздо больше проблем, чем это стоит.