python string case-insensitive

Ignorar caso en cadenas de Python



string case-insensitive (16)

Esta pregunta ya tiene una respuesta aquí:

¿Cuál es la forma más fácil de comparar cadenas en Python, ignorando el caso?

Por supuesto, uno puede hacer (str1.lower () <= str2.lower ()), etc., pero esto creó dos cadenas temporales adicionales (con los obvios gastos generales de alloc / gc).

Supongo que estoy buscando un equivalente al stricmp de C ().

[Un poco más de contexto solicitado, así que lo demostraré con un ejemplo trivial:]

Supongamos que quiere ordenar una larga lista de cadenas. Simplemente haces theList.sort (). Esto es O (n * log (n)) comparaciones de cadenas y ninguna gestión de memoria (ya que todas las cadenas y elementos de lista son algún tipo de punteros inteligentes). Usted es feliz.

Ahora, quiere hacer lo mismo, pero ignore el caso (simplifiquemos y diga que todas las cadenas son ascii, por lo que se pueden ignorar los problemas de configuración regional). Puede hacer theList.sort (clave = lambda s: s.lower ()), pero luego causa dos nuevas asignaciones por comparación, además de cargar al recolector de basura con las cadenas duplicadas (bajados). Cada uno de esos ruidos de administración de memoria es de órdenes de magnitud más lentos que la simple comparación de cadenas.

Ahora, con una función similar a stricmp in situ (), lo haces: theList.sort (cmp = stricmp) y es tan rápido y tan fácil de usar como theList.sort (). Eres feliz de nuevo.

El problema es que cualquier comparación insensible a mayúsculas y minúsculas basada en Python implica duplicaciones de cadenas implícitas, por lo que esperaba encontrar una comparación basada en C (quizás en una cadena de módulos).

No pude encontrar nada de eso, de ahí la pregunta aquí. (Espero que esto aclare la pregunta).


No puedo encontrar ninguna otra forma integrada de hacer una comparación insensible a mayúsculas y minúsculas: la receta de libros de cocina de python usa lower ().

Sin embargo, debe tener cuidado al usar las comparaciones inferiores debido al problema turco I. Desafortunadamente, el manejo de Python para Turkish Is no es bueno. ı se convierte en I, pero I no se convierte en ı. © se convierte a i, pero i no se convierte a İ.


Así es como lo harías con re:

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


En respuesta a tu aclaración ...

Puede usar ctypes para ejecutar la función c "strcasecmp". Ctypes está incluido en Python 2.5. Proporciona la capacidad de llamar a dll y bibliotecas compartidas como libc. Aquí hay un ejemplo rápido (Python en Linux, ver enlace para la ayuda de 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

también puede querer hacer referencia a la documentación de strcasecmp

No estoy seguro de si esto es más rápido o más lento (no lo he probado), pero es una forma de usar una función C para hacer comparaciones de cadenas que no distinguen entre mayúsculas y minúsculas.

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

Código de ActiveState - Receta 194371: Cadenas insensibles a mayúsculas / minúsculas es una receta para crear una clase de cadena insensible a mayúsculas y minúsculas. Puede ser un poco exagerado para algo rápido, pero podría proporcionarle una forma común de manejar cadenas insensibles a mayúsculas y minúsculas si planea usarlas con frecuencia.


Estoy bastante seguro de que debes usar .lower () o usar una expresión regular. No conozco una función de comparación de cadenas insensible a mayúsculas / minúsculas incorporada.


La expresión recomendada para ordenar listas de valores usando claves costosas para computar es para el llamado "patrón decorado". Consiste simplemente en crear una lista de tuplas (clave, valor) de la lista original y ordenar esa lista. Entonces es trivial eliminar las claves y obtener la lista de valores ordenados:

>>> 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'']

O si te gustan las frases ingeniosas:

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

Si realmente te preocupa el costo de llamar a lower (), puedes almacenar tuplas de (cadena baja, cadena original) en todas partes. Las tuplas son el tipo de contenedor más barato en Python, también son lavables, por lo que se pueden usar como claves de diccionario, miembros establecidos, etc.


Para las comparaciones ocasionales o incluso repetidas, algunos objetos de cadena adicionales no deberían importar siempre que esto no ocurra en el ciclo más interno de su código central o no tenga datos suficientes para notar realmente el impacto en el rendimiento. Vea si lo hace: hacer las cosas de una manera "estúpida" es mucho menos estúpido si también lo hace menos.

Si realmente quiere seguir comparando montones y montones de texto sin distinguir entre mayúsculas y minúsculas, puede mantener las versiones en minúsculas de las cadenas a mano para evitar la finalización y la recreación, o normalizar todo el conjunto de datos en minúsculas. Esto, por supuesto, depende del tamaño del conjunto de datos. Si hay relativamente pocas agujas y un gran pajar, reemplazar las agujas con objetos regexp compilados es una solución. Si es difícil de decir sin ver un ejemplo concreto.


Podrías subclasificar str y crear tu propia clase de cadenas insensatas, pero en mi humilde opinión eso sería extremadamente imprudente y crearía muchos más problemas de lo que valen la pena.


Podrías traducir cada cadena a minúsculas una vez, de forma perezosa solo cuando la necesites, o como un prepas para el género si sabes que ordenarás toda la colección de cadenas. Hay varias formas de adjuntar esta clave de comparación a los datos reales que se están ordenando, pero estas técnicas se deben abordar en un tema aparte.

Tenga en cuenta que esta técnica puede utilizarse no solo para resolver problemas de mayúsculas / minúsculas, sino también para otros tipos de ordenación, como la ordenación local específica o la clasificación de títulos "tipo biblioteca" que ignora los artículos principales y normaliza los datos antes de ordenarlos.


Su pregunta implica que no necesita Unicode. Pruebe el siguiente fragmento de código; si funciona para ti, ya terminaste:

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'']

Aclaración: en caso de que no sea obvio a primera vista, locale.strcoll parece ser la función que necesita, evitando str.lower o locale.strxfrm cadenas "duplicadas".


Solo use el método str().lower() , a menos que el alto rendimiento sea importante, en cuyo caso escriba ese método de clasificación como una extensión C.

"Cómo escribir una extensión de Python" parece una introducción decente ...

Lo que es más interesante, esta guía compara el uso de la biblioteca ctypes versus la escritura de un módulo C externo (el tipo c es bastante más lento que la extensión C).


Esta pregunta hace 2 cosas muy diferentes:

  1. ¿Cuál es la forma más fácil de comparar cadenas en Python, ignorando el caso?
  2. Supongo que estoy buscando un equivalente al stricmp de C ().

Ya que el # 1 ha sido respondido muy bien ya (es decir: str1.lower () <str2.lower ()) responderé # 2.

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

Solo use esta función cuando tenga sentido, ya que en muchos casos la técnica en minúsculas será superior.

Solo trabajo con ascii strings, no estoy seguro de cómo se comportará esto con Unicode.


No hay un built-in equivalente a la función que desea.

Puedes escribir tu propia función que convierta en .lower () cada carácter a la vez para evitar la duplicación de ambas cadenas, pero estoy seguro de que requerirá mucha CPU y será extremadamente ineficiente.

A menos que trabaje con cadenas extremadamente largas (tanto tiempo que puede causar un problema de memoria si está duplicado) entonces lo mantendría simple y usaría

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

Estarás bien


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


¿Está utilizando esta comparación en una ruta muy frecuentemente ejecutada de una aplicación altamente sensible al rendimiento? Alternativamente, ¿está ejecutando esto en cadenas que tienen megabytes de tamaño? De lo contrario, no debe preocuparse por el rendimiento y simplemente utilizar el método .lower ().

El siguiente código demuestra que hacer una comparación insensible a mayúsculas y minúsculas al llamar a .lower () en dos cadenas cuyo tamaño es de casi un megabyte toma aproximadamente 0.009 segundos en mi computadora de escritorio de 1.8GHz:

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

Si de hecho esta es una sección de código extremadamente importante y crítica para el rendimiento, entonces recomiendo escribir una función en C y llamarla desde su código Python, ya que eso le permitirá hacer una búsqueda verdaderamente eficiente y que no distinga entre mayúsculas y minúsculas. Los detalles sobre cómo escribir los módulos de extensión C se pueden encontrar aquí: https://docs.python.org/extending/extending.html


Aquí hay un punto de referencia que muestra que usar str.lower es más rápido que el método propuesto por la respuesta aceptada ( libc.strcasecmp ):

#/usr/bin/env python2.5 import random import timeit from ctypes import * libc = CDLL("libc.dylib") # change to "libc.so.6" on linux words = [word.rstrip() for word in open(''/usr/share/dict/words'', ''r'').readlines()] 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))

tiempos típicos en mi máquina:

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

Por lo tanto, la versión con str.lower no es solo la más rápida, sino también la más portátil y pitónica de todas las soluciones propuestas aquí. No he perfilado el uso de memoria, pero el póster original aún no me ha dado una razón convincente para preocuparse por ello. Además, ¿quién dice que una llamada al módulo libc no duplica cadenas?

NB: El método de cadena lower() también tiene la ventaja de ser dependiente de la configuración regional. Algo que probablemente no estarás acertando al escribir tu propia solución "optimizada". Aun así, debido a errores y funciones faltantes en Python, este tipo de comparación puede arrojar resultados erróneos en un contexto Unicode.


Cuando algo no se admite bien en la biblioteca estándar, siempre busco un paquete PyPI. Con la virtualización y la ubicuidad de las distribuciones Linux modernas, ya no evito las extensiones de Python. PyICU parece ajustarse a la factura: https://.com/a/1098160/3461

Ahora también hay una opción que es pura python. Está bien probado: https://github.com/jtauber/pyuca

Respuesta anterior:

Me gusta la solución de expresión regular. Aquí hay una función que puede copiar y pegar en cualquier función, gracias al soporte de estructura de bloques de python.

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

Como utilicé match en lugar de búsqueda, no necesité agregar un símbolo de intercalación (^) a la expresión regular.

Nota: Esto solo verifica la igualdad, que a veces es lo que se necesita. Tampoco llegaría tan lejos como para decir que me gusta.