대소 문자를 구분하지 않는 문자열 비교는 어떻게합니까?


573

파이썬에서 대소 문자를 구분하지 않는 문자열 비교를 어떻게 할 수 있습니까?

매우 간단하고 Pythonic 방식으로 일반 문자열과 저장소 문자열의 비교를 캡슐화하고 싶습니다. 또한 일반 파이썬 문자열을 사용하여 문자열로 해시 된 dict에서 값을 조회 할 수 있기를 원합니다.

답변:


595

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

71
항상 작동하지는 않습니다. 두 가지 그리스어 시그마가 있으며 그 중 하나는 끝에 만 사용된다는 것을 고려하십시오. 문자열 Σίσυφος (“Sísyphos”또는 더 나은“Síſyphos”)는 모두 앞에 대문자, 소문자 최종, 세 번째 위치에 소문자 비 최종의 세 가지가 있습니다. 두 문자열이 Σίσυφοςand ΣΊΣΥΦΟΣ인 경우 대소 문자를 구분하지 않고 동일한 대소 문자를 사용해야하기 때문에 접근 방식이 실패합니다.
tchrist

52
@ 마지막 두 주석 자 : 두 문자열을 ASCII 문자열이라고 가정하는 것이 공정하다고 생각합니다. 좀 더 흥미로운 것에 대한 답변을 찾고 있다면 거기에 있습니다 (또는 요청할 수 있음).
할리 홀콤

16
문제 : 'ß'.lower() == 'SS'.lower()거짓입니다.
kennytm

11
그리스 문자가 유일한 특별한 경우는 아닙니다! 미국 영어에서 문자 "i"(\ u0069)는 문자 "I"(\ u0049)의 소문자 버전입니다. 그러나 터키어 ( "tr-TR") 알파벳에는 "I with a dot"문자 "İ"(\ u0130)가 포함되어 있습니다.이 문자는 "i"의 대문자 버전이고 "I"는 "i없는 대문자 i"입니다. 점 "문자,"ı "(\ u0131).
Gqqnbig

20
@HarleyHolcombe 줄이 아스키 인 것으로 가정하는 것이 어떻게 안전합니까? 질문은 명시하지 않았으며 문자열이 사용자가 입력하거나 사용자에게 보여 주면 국제화를 지원해야합니다. 어쨌든, 새로운 프로그래머는 이것을 읽고있을 것이며 우리는 그들에게 진정한 정답을 주어야합니다.
Ethan Reesor

529

대소 문자를 구분하지 않는 방식으로 문자열을 비교하는 것은 사소한 것처럼 보이지만 그렇지 않습니다. 파이썬 2는 여기서 개발되지 않았기 때문에 파이썬 3을 사용할 것입니다.

가장 먼저 알아 두어야 할 것은 유니 코드로 대 / 소문자를 변환하는 변환은 쉽지 않다는 것입니다. 다음 text.lower() != text.upper().lower()과 같은 텍스트가 있습니다 "ß".

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

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

그러나 대소 문자를 비교 "BUSSE"하고 싶었다고 가정 해 봅시다 "Buße". 도대체 아마도 당신은 아마도 비교 "BUSSE"하고 "BUẞE"평등 하기를 원할 것입니다 -그것이 새로운 자본 형태입니다. 권장되는 방법은 다음을 사용하는 것입니다 casefold.

str. 케이스 ()

문자열의 대소 문자를 복사하십시오. 대소 문자를 구분하는 문자열은 대소 문자를 구분하기 위해 사용할 수 있습니다.

케이스 폴딩은 소문자와 비슷하지만 문자열에서 모든 케이스 구별을 제거하기 때문에 더 공격적입니다. [...]

그냥 사용하지 마십시오 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 normalization 을 사용하고 싶지만 설명서를 자유롭게 확인하십시오. 그런 다음 하나

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)

8
더 나은 해결책은 섭취시 모든 문자열을 정규화하는 것 x.casefold() == y.casefold()입니다. 대소 문자를 구분하지 않는 비교 (대소 문자 x == y구분)를 수행하면됩니다.
abarnert

3
@abarnert 사실, 상황에 따라-때때로 소스를 그대로 두는 것이 좋지만 사전에 정규화하면 나중에 코드를 훨씬 간단하게 만들 수 있습니다.
Veedrac

3
@ Veedrac : 당신이 맞아요, 항상 적절한 것은 아닙니다. 원래 소스를 변경하지 않고 출력 할 수 있어야하는 경우 (예 : NKFC와 NKFD가 모두 허용되고 명시 적으로 달라지는 Linux에서 파일 이름을 처리하기 때문에) 입력시 변환 할 수 없습니다.
abarnert

7
유니 코드 표준 섹션 3.13에는 대소 문자를 비교하지 않는 두 가지 다른 정의가 있습니다. (D146, 표준) NFD(toCasefold(NFD(str)))양쪽에 (D147, 호환성) NFKD(toCasefold(NFKD(toCasefold(NFD(X))))). 내부 NFD는 그리스의 특정 억양 문자 만 처리 할 수 있다고 명시 되어 있습니다. 나는 그것이 모든 경우에 관한 것이라고 생각합니다.

2
그리고 casefold ()가 대문자로 바뀌는 체로키 알파벳에 약간의 재미가 있습니다. >>> "ᏚᎢᎵᎬᎢᎬᏒ". upper () 'ᏚᎢᎵᎬᎢᎬᏒ'>>> "ᏚᎢᎵᎬᎢᎬᏒ". lower () 'ꮪꭲꮅꭼꭲꭼꮢ'>>> "ᏚᎢᎵᎬᎢᎬᏒ" .casefold () 'ᏚᎢᎵᎬᎢᎬᏒ'>>>
bortzmeyer

60

Python 2를 사용하여 .lower()각 문자열 또는 유니 코드 객체를 호출 합니다 ...

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

... 대부분의 시간 동안 작동하지만 실제로 @tchrist가 설명한 상황 에서는 작동하지 않습니다 .

unicode.txt두 개의 문자열 Σίσυφος과를 포함 하는 파일이 있다고 가정합니다 ΣΊΣΥΦΟΣ. 파이썬 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은 위의 인터프리터 출력물에 표시되어 있습니다.)


20
Python 3.3부터는 비교를 더욱 강력하게하기 위해 casefold를 사용할 수 있습니다 (예 : first.casefold () == second.casefold ()). : 파이썬 2의 당신은 PyICU (도 볼 수 있습니다 icu-project.org/apiref/icu4c/...를 )
kgriffs

42

유니 코드 표준의 섹션 3.13은 대소 문자를 구분하지 않는 알고리즘을 정의합니다.

X.casefold() == Y.casefold() 파이썬 3에서는 "기본 대소 문자 일치하지 않음"(D144)을 구현합니다.

케이스 폴딩은 모든 경우에 문자열 정규화를 유지하지 않으므로 정규화를 수행해야합니다 ( 'å'vs 'å'). 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

'㎒'(U + 3392) 및 "식별자 케이스리스 매칭"과 같은 경우에 대한 케이스리스 매칭 (D146) 호환성이있어 식별자의 케이스리스 매칭을 단순화하고 최적화 할 있습니다.


3
Python 3은 유니 코드 문자열을 사용하고 유니 코드 표준은 대소 문자를 구분하지 않는 문자열 일치를 정의하는 방법을 설명하므로 이는 Python 3에 가장 적합한 답변입니다.
SergiyKolesnikov

불행히도 Python 3.6부터는 casefold()함수가 Casefolding Properties에 설명 된대로 대문자 I 및 ​​점선 대문자 I의 특수한 처리를 구현하지 않습니다 . 따라서 해당 문자가 포함 된 투르크 언어의 단어는 비교에 실패 할 수 있습니다. 예를 canonical_caseless('LİMANI') == canonical_caseless('limanı')들어을 반환해야 True하지만을 반환합니다 False. 현재 파이썬에서 이것을 처리하는 유일한 방법은 casefold 래퍼를 작성하거나 PyICU와 같은 외부 유니 코드 라이브러리를 사용하는 것입니다.
SergiyKolesnikov

@ SergiyKolesnikov .casefold ()는 내가 알 수있는 한 동작합니다. 표준에서 : "기본 케이싱 작업은 특정 언어 및 환경에 맞게 조정되지 않은 상태 에서 사용하기위한 것입니다 . " 터키의 점선 대문자 I 및 ​​점이없는 작은 i에 대한 대소 문자 규칙은 SpecialCasing.txt에 있습니다. "터키어가 아닌 언어의 경우이 매핑은 일반적으로 사용되지 않습니다." 유니 코드 FAQ : Q : 터키어에 대해 로캘 독립적 인 케이싱을 지원하기 위해 추가 문자가 인코딩되지 않은 이유는 무엇입니까?
jfs

1
@ jf-sebastian 나는 casefold ()가 잘못 작동한다고 말하지 않았다. 대문자와 점선으로 된 대문자 I의 특수 처리를 가능하게하는 선택적 매개 변수를 구현 한 경우 실용적입니다. 예를 들어, ICU 라이브러리의 foldCase ()가 수행하는 방식 은 다음과 같습니다. -민감하지만 CaseFolding.txt에서 'T'로 표시된 점이있는 I 및 점이없는 i에 대한 매핑을 포함할지 또는 제외할지에 대한 옵션이 있습니다. "
SergiyKolesnikov

6

나는이 솔루션을보고 여기에 사용 정규식 .

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

악센트와 잘 어울립니다.

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

그러나 대소 문자를 구분하지 않는 유니 코드 문자에는 작동하지 않습니다. 사실을 이해하기 위해서는 정확한 기호가 필요하다는 것을 이해했기 때문에 @Rhymoid에게 감사드립니다. 출력은 다음과 같습니다.

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
대소 문자를 구분하지 않는 검색 ß에서 찾을 수 없다는 사실 은 유니 코드 문자 작동 하지 않는다는SS 증거입니다 .

3

조회 및 비교를 위해 문자열을 대문자 또는 소문자로 사용하는 것이 일반적입니다. 예를 들면 다음과 같습니다.

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

2

먼저 소문자로 변환하는 것은 어떻습니까? 사용할 수 있습니다 string.lower().


4
당신은 그들의 소문자 매핑 비교할 수 ΣίσυφοςΣΊΣΥΦΟΣ해당 테스트 않겠지 만해야한다.
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
stdout에 인쇄 된 메시지로 예외를 대체 한 다음 없음을 리턴합니다 (False). 실제로는 매우 도움이되지 않습니다.
gerrit

-2

두 문자열을 소문자로 변환하고 (모든 문자가 소문자가 됨) 문자열을 ASCII 문자열이라고 가정하면됩니다.

예를 들면 다음과 같습니다.

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

이 답변은 새로운 정보를 추가하지 않습니다. 또한 허용되는 답변 과 거의 동일 합니다 .
조지

-3

이것은 지난 주에 사랑 / 증오하는 법을 배운 또 다른 정규 표현식이므로 일반적으로 임 느낌을 반영하는 것으로 가져옵니다 (이 경우 예). 정상적인 기능을 만드십시오 .... 입력을 요청한 다음 .... something = re.compile (r'foo * | spam * ', yes.I) ... re.I (yes.I 아래)는 IGNORECASE와 동일하지만 작성하는 데 많은 실수를 할 수는 없습니다!

그런 다음 정규 표현식을 사용하여 메시지를 검색하지만 솔직히 자체 페이지가 있어야하지만 요점은 foo 또는 스팸이 함께 파이프되고 대소 문자가 무시된다는 것입니다. 그러면 둘 중 하나가 발견되면 lost_n_found가 그 중 하나를 표시합니다. 둘 다 없으면 lost_n_found가 None과 같습니다. none이 아닌 경우 "return lost_n_found.lower ()"를 사용하여 소문자로 user_input을 리턴하십시오.

이를 통해 대소 문자를 구분하는 항목을 훨씬 쉽게 일치시킬 수 있습니다. 마지막으로 (NCS)는 "아무도 신경 쓰지 않습니다 ...!"의 약자입니다. 대소 문자를 구분하지 않습니다.

질문이 있으시면 저에게 연락하십시오 ..

    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")
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.