it-swarm-eu.dev

Ignorovat případ v řetězcích Python

Jaký je nejjednodušší způsob porovnání řetězců v Pythonu, ignorování případu?

Samozřejmě se dá dělat (str1.lower () <= str2.lower ()), atd., Ale vytvořily se další dva dočasné řetězce (se zřejmými režijními náklady aloc/g-c).

Myslím, že hledám ekvivalent stricmp C ().

[Některé další kontext je vyžadován, takže budu demonstrovat s triviálním příkladem:]

Předpokládejme, že chcete třídit seznam řetězců. Jednoduše provedete theList.sort (). Toto je O (n * log (n)) porovnání řetězců a žádná správa paměti (protože všechny řetězce A elementy seznamu jsou nějakým druhem inteligentních ukazatelů) . Jste spokojeni.

Nyní chcete udělat to samé, ale případ ignorujte (zjednodušíme a řekneme Všechny řetězce jsou ascii, takže problémy s locale mohou být ignorovány). Můžete udělat theList.sort (klíč = lambda s: s.lower ()), ale pak způsobíte dvě nová alokace na srovnání, plus zatěžujete sběrač odpadů duplikovanými (sníženými) řetězci. Každý takový šum správy paměti je řádově pomalejší než jednoduché porovnání řetězců.

Nyní, s místním stricmp () - jako funkce, děláte: theList.sort (cmp = stricmp) A je stejně rychlý a jako paměť-přátelský jako theList.sort (). Znovu jsi šťastná.

Problém je, že Pythonovo případové necitlivé porovnání zahrnuje implicitní duplikaci řetězce , Takže jsem očekával, že najdeme srovnání založená na C (možná v řetězci modulu).

Nemohl jsem nic takového najít, proto je tu otázka. (Doufám, že to vyjasňuje otázku).

51
Paul Oyster

Zde je měřítko, které ukazuje, že použití str.lower je rychlejší než navržená metoda přijaté odpovědi (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))

typické časy na mém stroji:

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

Takže verze s str.lower je nejen nejrychlejší zdaleka, ale také nejprenosnější a nejpohodlnější ze všech navrhovaných řešení zde. Nezpracoval jsem využití paměti, ale původní plakát stále ještě neposkytl přesvědčivé výsledky. důvodem k obavám. Kdo říká, že volání do modulu libc nekopíruje žádné řetězce?

Poznámka: Metoda řetězce lower() má také výhodu, že závisí na místním nastavení. Něco, co pravděpodobně nebudete mít pravdu při psaní vlastního "optimalizovaného" řešení. I tak, kvůli chybám a chybějícím funkcím v Pythonu, může tento druh srovnání způsobit nesprávné výsledky v kontextu Unicode.

74
user3850

Používáte toto porovnání ve velmi často prováděné cestě aplikace s vysokým výkonem? Nebo používáte toto na řetězcích, které jsou velikosti megabajtů? Pokud ne, neměli byste se starat o výkon a pouţít metodu .lower ().

Následující kód demonstruje, že provedení nerozlišování velkých a malých písmen porovnáním volání .lower () na dvou řetězcích, z nichž každý má velikost téměř megabajtů, trvá na mém stolním počítači 1,8 GHz přibližně 0,009 sekundy:

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

Pokud se jedná o extrémně významnou, výkonově kritickou část kódu, pak doporučuji zapsat funkci v jazyce C a zavolat ji z vašeho Pythonova kódu, protože to vám umožní provést skutečně efektivní vyhledávání v malých a velkých písmenech. Podrobnosti o psaní rozšiřujících modulů C naleznete zde: https://docs.python.org/extending/extending.html

7
Eli Courtwright

Vaše otázka znamená, že nepotřebujete Unicode. Vyzkoušejte následující fragment kódu; pokud to funguje pro vás, jste hotovi:

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

Vyjasnění: v případě, že není na první pohled zřejmé, locale.strcoll se zdá být funkcí, kterou potřebujete, vyhnete se str.lower nebo locale.strxfrm "duplicitním" řetězcům.

7
tzot

Nemohu najít žádný jiný vestavěný způsob, jak dělat případ-necitlivé srovnání: python kuchařská kniha recept používá nižší ().

Nicméně musíte být opatrní, když používáte nižší pro srovnání kvůli tureckému I problému . Pythonova manipulace s tureckým Is není bohužel dobrá. ı se převede na I, ale já se nepřevede na ı. © je převeden na i, ale i není převeden na İ. 

5
Douglas Leeder

Neexistuje žádný vestavěný ekvivalent této funkce, kterou chcete.

Můžete napsat svou vlastní funkci, která převádí na .lower () každý znak najednou, aby nedošlo k duplikaci obou řetězců, ale jsem si jistý, že to bude velmi náročné na procesory a extrémně neefektivní. 

Pokud pracujete s extrémně dlouhými řetězci (tak dlouho, že mohou způsobit problémy s pamětí, pokud jsou duplikovány), pak bych to udržel jednoduchý a používal 

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

Budeš v pořádku

3
Ricardo Reyes

Tato otázka se ptá 2 velmi odlišných věcí:

  1. Jaký je nejjednodušší způsob porovnání řetězců v Pythonu, ignorování případu?
  2. Myslím, že hledám ekvivalent stricmp C ().

Vzhledem k tomu, že # 1 již bylo zodpovězeno velmi dobře (např .: str1.lower () <str2.lower ()) odpovím # 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

Tuto funkci používejte pouze tehdy, když má smysl, protože v mnoha případech bude malá technika lepší.

Pracuji pouze s řetězci ascii, nejsem si jistý, jak se to bude chovat s unicode.

2
trevorcroft

Když něco není ve standardní knihovně dobře podporováno, vždy hledám balíček PyPI. S virtualizací a všudypřítomností moderních linuxových distribucí se již vyhýbám rozšíření Pythonu. Zdá se, že PyICU zapadá do účtu: https://stackoverflow.com/a/1098160/3461

Tam je nyní také možnost, která je čistá python. Je to dobře testováno: https://github.com/jtauber/pyuca


Stará odpověď:

Líbí se mi řešení regulárního výrazu. Zde je funkce, kterou můžete kopírovat a vkládat do libovolné funkce díky podpoře struktury pythonových bloků.

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

Vzhledem k tomu, že jsem místo vyhledávání použil zápas, nemusel jsem k regulárnímu výrazu přidávat stříbro (^).

Poznámka: Toto pouze kontroluje rovnost, která je někdy potřebná. Také bych nešel tak daleko, abych řekl, že se mi to líbí.

2
Benjamin Atkin

Doporučený způsob třídění seznamů hodnot pomocí drahých klíčů je tzv. „Zdobený vzor“. Skládá se jednoduše z sestavení seznamu (klíčových, hodnotových) n-tic z původního seznamu a třídění seznamu. Pak je triviální odstranit klíče a získat seznam seřazených hodnot:

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

Nebo pokud máte rádi jednovrstvé vložky:

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

Pokud se opravdu obáváte nákladů na volání nižší (), můžete si všude ukládat n-tice (snížený řetězec, původní řetězec). Tuply jsou v Pythonu nejlevnějším druhem kontejnerů, jsou také hashable, takže mohou být použity jako klíčová slova, členy sady atd.

1
Antoine P.

Takto byste to dělali s re:

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

Pro občasné nebo dokonce opakované porovnávání by neměla být několik dalších řetězcových objektů důležitá, pokud se to nestane v nejvnitřnější smyčce vašeho jádrového kódu, nebo nemáte dostatek dat, abyste si mohli všimnout dopadu na výkon. Uvidíme, jestli to děláte: dělat věci v "hloupé" cestě je mnohem méně hloupé, pokud to také děláte méně.

Pokud si vážně chcete nechat porovnávat spousty a spoustu textově necitlivých textů, můžete po ruce držet malá verze řetězců, abyste se vyhnuli finalizaci a opětovnému vytvoření, nebo normalizujte celou sadu dat na malá písmena. To samozřejmě závisí na velikosti datového souboru. Pokud existuje relativně málo jehel a velká kupka sena, je nahrazení jehel zkompilovanými objekty regexp jedním řešením. Pokud je těžké říct, aniž by viděl konkrétní příklad.

0
yason

Můžete překládat každý řetězec na malá písmena jednou - jen líně, jen když to potřebujete, nebo jako předprase k druhu, pokud víte, že budete třídit celou sbírku řetězců. Existuje několik způsobů, jak připojit tento porovnávací klíč k aktuálním datům, které jsou tříděny, ale tyto techniky by měly být řešeny v samostatném problému.

Všimněte si, že tato technika může být použita nejen pro zpracování velkých/malých písmen, ale i pro jiné typy třídění, jako je například třídění podle národního prostředí nebo třídění podle názvu knihovny, které ignoruje přední články a jinak normalizuje data před jejich tříděním.

0
Dale Wilson

Stačí použít metodu str().lower(), pokud není důležitý vysoký výkon - v takovém případě zapište tuto metodu třídění jako rozšíření C.

"Jak psát Python Extension" vypadá jako slušné intro ..

Ještě zajímavější je, že Tato příručka porovnává použití knihovny ctypes versus psaní externího C modulu (ctype je poměrně podstatně pomalejší než rozšíření C).

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

Jsem si jistý, že musíte použít .lower () nebo použít regulární výraz. Nejsem si vědom vestavěného case-insensitive funkce porovnání řetězců.

0
Mark Biek