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


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.