Büyük / küçük harfe duyarlı olmayan bir dize karşılaştırması nasıl yaparım?


573

Python'da büyük / küçük harf duyarlı olmayan dize karşılaştırmasını nasıl yapabilirim?

Ben çok basit ve Pythonic bir şekilde kullanarak bir depo dize normal dizeleri karşılaştırma kapsüllemek istiyorum. Ayrıca normal python dizeleri kullanarak dizeleri tarafından karma bir dikte değerleri aramak için yeteneği istiyorum.

Yanıtlar:


595

ASCII dizelerini varsayarsak:

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)")

71
Bu her zaman işe yaramaz. Örneğin, sadece sonunda kullanılan iki Yunan sigması olduğunu düşünün. Dize Σίσυφος ( “Sisyphos”, ya da daha iyi “Sisyphos”) her üç sahiptir: Önde büyük harf, sonunda nihai küçük harf ve üçüncü pozisyonda nonfinal küçük harfli. Eğer iki dizeniz Σίσυφοςve ise ΣΊΣΥΦΟΣ, o zaman yaklaşımınız başarısız olur, çünkü bunların aynı durumun duyarsızca olması gerekiyordu.
tchrist

52
@ Son iki yorumcu: Her iki dizenin de ascii dizeleri olduğunu varsaymanın adil olduğunu düşünüyorum. Biraz daha heyecan verici bir şeye cevap arıyorsanız, eminim orada (ya da sorabilirsiniz).
Harley Holcombe

16
Sorun: 'ß'.lower() == 'SS'.lower()Yanlış.
kennytm

11
Yunan harfleri tek özel durum değildir! ABD İngilizcesinde "i" (\ u0069) karakteri, "I" (\ u0049) karakterinin küçük harfli sürümüdür. Ancak, Türkçe ("tr-TR") alfabesi "i" noktalı "I" (\ u0130) içerir, "i" nin büyük versiyonu ve "I" ise "i olmadan nokta "karakteri", "ı" (\ u0131).
Gqqnbig

20
@HarleyHolcombe Dizelerin ascii olduğunu varsaymak nasıl güvenli (veya adil)? Soru belirtilmedi ve dizeler bir kullanıcı tarafından girilmiş veya bir kullanıcıya gösterilmişse, uluslararasılaştırmayı desteklemelisiniz. Ne olursa olsun, yeni programcılar bunu okuyacak ve onlara gerçekten doğru cevabı vermeliyiz.
Ethan Reesor

529

Dizeleri büyük / küçük harfe duyarlı olmayan bir şekilde karşılaştırmak önemsiz görünüyor, ama değil. Python 3 kullanacağım, çünkü Python 2 burada az gelişmiş.

Unutulmaması gereken ilk şey, Unicode'daki vaka kaldırma dönüşümlerinin önemsiz olmadığıdır. Metin var text.lower() != text.upper().lower()gibi "ß":

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

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

Ama diyelim ki dikkatsizce karşılaştırmak istediniz "BUSSE"ve "Buße". Heck, muhtemelen karşılaştırmak "BUSSE"ve "BUẞE"eşitlemek istersiniz - bu daha yeni sermaye formudur. Önerilen yol aşağıdakileri kullanmaktır casefold:

str. casefold ()

Dizenin büyük harfli bir kopyasını döndürür. Kazasız dizeler, kasasız eşleştirme için kullanılabilir.

Casefolding, küçük harflere benzer, ancak daha agresiftir, çünkü bir dizedeki tüm vaka ayrımlarını kaldırmayı amaçlamaktadır. [...]

Sadece kullanmayın lower. Eğer casefold, kullanılamaz yapıyor .upper().lower()(ama sadece biraz) yardımcı olur.

O zaman aksanları düşünmelisiniz. Yazı tipi oluşturucunuz iyiyse, muhtemelen düşünürsünüz "ê" == "ê"- ancak yapmaz:

"ê" == "ê"
#>>> False

Bunun nedeni, ikincisindeki aksanın birleştirici bir karakter olmasıdır.

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

Bununla baş etmenin en basit yolu unicodedata.normalize. Muhtemelen NFKD normalleştirmesini kullanmak istiyorsunuz , ancak belgeleri kontrol etmekten çekinmeyin. Sonra biri

unicodedata.normalize("NFKD", "ê") == unicodedata.normalize("NFKD", "ê")
#>>> True

Tamamlamak için, burada bu fonksiyonlarda ifade edilir:

import unicodedata

def normalize_caseless(text):
    return unicodedata.normalize("NFKD", text.casefold())

def caseless_equal(left, right):
    return normalize_caseless(left) == normalize_caseless(right)

8
Daha iyi bir çözüm, alımdaki tüm dizelerinizi normalleştirmektir, o zaman sadece büyük / x.casefold() == y.casefold()küçük harfe duyarlı olmayan karşılaştırmalar (ve daha da önemlisi, x == ybüyük / küçük harfe duyarlı için) yapabilirsiniz.
abarnert

3
@abarnert Gerçekten de, bağlama bağlı olarak - bazen kaynağı olduğu gibi bırakmak daha iyidir, ancak açık normalleştirme daha sonraki kodları daha basit hale getirebilir.
Veedrac

3
@Veedrac: Haklısın, her zaman uygun değil; orijinal kaynağı değişmeden verebilmeniz gerekiyorsa (örneğin, Linux'ta, NKFC ve NKFD'nin hem izin verildiği hem de açıkça farklı olması gerektiği dosya adları ile uğraştığınız için), açıkça girişte dönüştüremezsiniz ...
abarnert

7
Unicode Standart bölüm 3.13'te örneksiz karşılaştırmalar için iki tanım bulunmaktadır: NFD(toCasefold(NFD(str)))her iki tarafta (D146, kanonik) ve her iki tarafta (D147, uyumluluk) NFKD(toCasefold(NFKD(toCasefold(NFD(X))))). İç NFDkısmın sadece belirli bir Yunan aksan karakterini ele alacağını belirtiyor . Sanırım her şey son durumlarla ilgili.

2
Ve casefold () büyük harfe giden Cherokee alfabesiyle biraz eğlenceli: >>> "ᏚᎢᎵᎬᎢᎬᏒ". Upper () 'ᏚᎢᎵᎬᎢᎬᏒ' >>> "ᏚᎢᎵᎬᎢᎬᏒ". Alt () 'ꮪꭲꮅꭼꭲꭼꮢ' >>> "ᏚᎢᎵᎬᎢᎬᏒ" .casefold () 'ᏚᎢᎵᎬᎢᎬᏒ' >>>
bortzmeyer

60

Python 2'yi kullanarak .lower(), her bir dizeyi veya Unicode nesnesini çağırır ...

string1.lower() == string2.lower()

... çoğu zaman işe yarayacaktır, ama gerçekten de @tchrist'in tanımladığı durumlarda çalışmaz .

Biz adlı bir dosya var varsayalım unicode.txtiki dizeleri içeren Σίσυφοςve ΣΊΣΥΦΟΣ. Python 2 ile:

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

Σ karakterinin ς ve σ olmak üzere iki küçük harfi vardır ve büyük / küçük harfe .lower()duyarlı olmayan şekilde karşılaştırılmasına yardımcı olmaz.

Ancak, Python 3'ten itibaren her üç form da ς olarak çözümlenecek ve her iki dizede daha düşük () çağrılması düzgün çalışacaktır:

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

Yunanca üç sigma gibi kenar durumları önemsiyorsanız, Python 3'ü kullanın.

(Referans olarak, Python 2.7.3 ve Python 3.3.0b1 yukarıdaki yorumlayıcı çıktılarında gösterilmiştir.)


20
Karşılaştırmayı daha da sağlam hale getirmek için Python 3.3'ten başlayarak casefold kullanabilirsiniz (örn. First.casefold () == second.casefold ()). Python 2 için PyICU'yu kullanabilirsiniz (ayrıca bkz: icu-project.org/apiref/icu4c/… )
kgriffs

42

Unicode standardının 3.13. Bölümü , örneksiz eşleştirme için algoritmaları tanımlar.

X.casefold() == Y.casefold() Python 3'te "varsayılan örneksiz eşleştirme" (D144) uygulanmaktadır.

Casefolding, tüm durumlarda dizelerin normalleşmesini korumaz ve bu nedenle normalleştirmenin yapılması gerekir ( 'å'vs. 'å'). D145 "standart kanonik eşleştirme" özelliğini sunar:

import unicodedata

def NFD(text):
    return unicodedata.normalize('NFD', text)

def canonical_caseless(text):
    return NFD(NFD(text).casefold())

NFD() U + 0345 karakterini içeren çok seyrek görülen kenar vakaları için iki kez çağrılır.

Misal:

>>> 'å'.casefold() == 'å'.casefold()
False
>>> canonical_caseless('å') == canonical_caseless('å')
True

Ayrıca gibi durumlar için uyumluluk Kovansız eşleştirme (D146) vardır '㎒'kolaylaştırmak için (U + 3392) ve "tanımlayıcı Kovansız eşleştirme" ve optimize tanımlayıcıları Kovansız eşleme .


3
Bu Python 3 için en iyi yanıttır, çünkü Python 3 Unicode dizeleri kullanır ve cevap Unicode standardının örneksiz dizgi eşleştirmesini nasıl tanımladığını açıklar.
SergiyKolesnikov

Ne yazık ki, Python 3.6'dan itibaren, bu casefold()fonksiyon Büyük Harf Katsayısı ve noktalı Büyük Harf I'in özel durum tedavisini Vaka Katlama Özellikleri'nde açıklandığı gibi uygulamamaktadır . Bu nedenle, bu harfleri içeren Türk dillerinden kelimeler için karşılaştırma başarısız olabilir. Örneğin, canonical_caseless('LİMANI') == canonical_caseless('limanı')geri dönmelidir True, ancak geri döner False. Şu anda, Python'da bununla başa çıkmanın tek yolu, bir vaka dosyası sarıcısı yazmak veya PyICU gibi harici bir Unicode kütüphanesi kullanmaktır.
SergiyKolesnikov

@SergiyKolesnikov .casefold () anlatabildiğim kadarıyla davranır. Standarttan: "varsayılan kasa işlemleri, belirli diller ve ortamlar için terzilik yapılmadan kullanılmak üzere tasarlanmıştır " . Türk noktalı büyük I ve küçük küçük i için kasa kuralları SpecialCasing.txt içindedir. "Türk olmayan diller için bu eşleme normalde kullanılmaz." Unicode SSS'den: S: Neden Türkçe için yerel ayardan bağımsız kasayı desteklemek üzere kodlanmış fazladan karakterler yok?
jfs

1
@ jf-sebastian Ben casefold () hatalı davrandığını söylemedim. Büyük ve noktalı büyük I özel işlemesini sağlayan isteğe bağlı bir parametre uygularsa pratik olur. Örneğin , YBÜ kitaplığındaki foldCase () yöntemi bunu yapar : "Büyük / küçük harf katlama yerelden bağımsızdır ve bağlamdan bağımsızdır -sensitive, ancak CaseFolding.txt dosyasında 'T' ile işaretlenmiş noktalı I ve noktalı i için eşleme ekleme veya hariç tutma seçeneği vardır. "
SergiyKolesnikov

6

Bu çözümü burada regex kullanarak gördüm .

import re
if re.search('mandy', 'Mandy Pande', re.IGNORECASE):
# is True

Aksanlarla iyi çalışır

In [42]: if re.search("ê","ê", re.IGNORECASE):
....:        print(1)
....:
1

Ancak, büyük / küçük harf duyarsız unicode karakterlerle çalışmaz. @Rhymoid, anlayışımın tam olarak sembol olması gerektiğinden, davanın doğru olması gerektiğine dikkat çektiğiniz için teşekkür ederim. Çıktı aşağıdaki gibidir:

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)
....:

4
Aslında ßiçinde bulunmazsa SSharf duyarsız arama ile o kanıtıdır çalışmıyor Unicode karakterleri ile çalışmalarını hiç .

3

Genel yaklaşım dizeleri büyük harflerle veya aramalar ve karşılaştırmalar için küçük harflerle ifade etmektir. Örneğin:

>>> "hello".upper() == "HELLO".upper()
True
>>> 

2

Önce küçük harfe dönüştürmeye ne dersiniz? kullanabilirsiniz string.lower().


4
Küçük harfli haritalarını karşılaştıramazsınız: Σίσυφοςve ΣΊΣΥΦΟΣeşdeğerini test etmez, ancak denemelidir.
tchrist

-2
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__)

3
Bir istisnayı stdout'a yazdırılmış bir mesajla değiştiriyorsunuz, sonra Yok'u (yanlış) döndürüyorsunuz. Bu pratikte çok yararsızdır.
gerrit

-2

Tek yapmanız gereken iki dizeyi küçük harfe dönüştürmek (tüm harfler küçük harfe dönüşür) ve sonra bunları karşılaştırmak (dizelerin ASCII dizeleri olduğu varsayılarak).

Örneğin:

string1 = "Hello World"
string2 = "hello WorlD"

if string1.lower() == string2.lower():
    print("The two strings are the same.")
else:
    print("The two strings are not the same.")

Bu cevap yeni bilgi eklemiyor. Dahası, neredeyse kabul edilen cevapla aynı .
Georgy

-3

Bu, geçen hafta sevmeyi / nefret etmeyi öğrendiğim başka bir normaldir, bu yüzden genellikle bu hissi yansıtan bir şey olarak (bu durumda evet) içe aktarın! normal bir işlev yapın .... giriş isteyin, sonra kullanın .... bir şey = re.compile (r'foo * | spam * ', evet.I) ...... re.I (evet.I aşağıda) IGNORECASE ile aynıdır, ancak yazarken çok fazla hata yapamazsınız!

Daha sonra normal ifadeler kullanarak mesajınızı ararsınız, ancak dürüst olmak gerekirse bu, kendi başına birkaç sayfa olmalıdır, ancak asıl nokta, foo veya spam'in bir araya getirilmesi ve vakanın yok sayılmasıdır. Sonra her ikisi de bulunursa lost_n_found bunlardan birini görüntüler. eğer ikisi de lost_n_found Yok'a eşitse. Hiçbirine eşit değilse, "return lost_n_found.lower ()" kullanarak user_input değerini küçük harfle döndürür

Bu, büyük / küçük harfe duyarlı olacak her şeyi daha kolay eşleştirmenizi sağlar. Son olarak (NCS) "kimse ciddiye almaz ...!" veya büyük / küçük harfe duyarlı değil ... hangisi olursa olsun

herhangi bir sorunuz varsa beni bu konuda ele geçirin ..

    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")
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.