대소 문자를 구분하지 않는 '입력'


151

나는 표현을 좋아한다

if 'MICHAEL89' in USERNAMES:
    ...

USERNAMES목록은 어디에 있습니까 ?


대소 문자를 구분하지 않는 항목을 일치시키는 방법이 있습니까? 아니면 사용자 지정 방법을 사용해야합니까? 이를 위해 추가 코드를 작성해야하는지 궁금합니다.

답변:


179
username = 'MICHAEL89'
if username.upper() in (name.upper() for name in USERNAMES):
    ...

또는

if username.upper() in map(str.upper, USERNAMES):
    ...

또는 사용자 정의 방법을 만들 수 있습니다.


8
if 'CaseFudge'.lower() in [x.lower() for x in list]
fredley September

44
[...]전체 목록을 만듭니다. (name.upper() for name in USERNAMES)한 번에 생성기와 하나의 필요한 문자열 만 생성합니다.이 작업을 많이 수행하면 메모리를 크게 절약 할 수 있습니다. (매번 확인하기 위해 재사용하는 소문자 사용자 이름 목록을 작성하면 더 많은 비용 절감 효과)
viraptor

2
성능상의 이유로 dict를 작성할 때는 모든 키를 낮추는 것이 좋습니다.
Ryan

1
[목록의 x에 대한 [x.lower ()]가 목록 이해인 경우 (USERNAMES의 이름에 대한 name.upper ())는 튜플 이해입니까? 아니면 다른 이름이 있습니까?
otocan

1
@otocan 제너레이터 표현식입니다.
nmichaels

21

난 당신이 비 침습적 수 있도록 래퍼를 만들 것 입니다. 최소한, 예를 들어 ... :

class CaseInsensitively(object):
    def __init__(self, s):
        self.__s = s.lower()
    def __hash__(self):
        return hash(self.__s)
    def __eq__(self, other):
        # ensure proper comparison between instances of this class
        try:
           other = other.__s
        except (TypeError, AttributeError):
          try:
             other = other.lower()
          except:
             pass
        return self.__s == other

이제 if CaseInsensitively('MICHAEL89') in whatever:필요에 따라 동작해야합니다 (오른쪽이 목록, dict 또는 set인지 여부). (문자열 포함에 대해 비슷한 결과를 얻으려면 더 많은 노력이 필요할 수 있습니다 unicode.


3
{ 'Michael89': True} : print "found"의 CaseInsensitively ( 'MICHAEL89')
Xavier Combelle

2
Xavier : "필요 CaseInsensitively('MICHAEL89') in {CaseInsensitively('Michael89'):True}에 따라 동작"에 해당하지 않는 작동해야합니다.
Gabe

그것을 할 수있는 확실한 방법이 하나뿐입니다. 많이 사용하지 않으면 무겁습니다. 그것은 매우 매끄 럽습니다.
nmichaels

2
@Nathon, 컨테이너를 침습적으로 변경하는 것은 "무겁게 느껴지는"작업 인 것 같습니다. 완전히 비 침습적 인 포장지 : 이것이 얻을 수있는 것보다 "가벼운"정도! 별로 ;-). @Xavier, RHS는 대소 문자가 혼합 된 키 / 항목이 포함 된 세트 또는 세트로, 비 침습적 래퍼 (단순 etc.하고 "더 많은 노력이 필요합니다"부분);
Alex Martelli

헤비에 대한 저의 정의는 한 번만 사용되는 것을 만들기 위해 약간의 코드를 작성하는 것입니다. 이것이 두 번 이상 사용될 예정이라면 완벽하게 합리적입니다.
nmichaels

12

일반적으로 (적어도 적어도) 원하는 방식으로 동작하도록 객체를 형성합니다. name in USERNAMES대소 문자를 구분하지 않으므로 USERNAMES변경해야합니다.

class NameList(object):
    def __init__(self, names):
        self.names = names

    def __contains__(self, name): # implements `in`
        return name.lower() in (n.lower() for n in self.names)

    def add(self, name):
        self.names.append(name)

# now this works
usernames = NameList(USERNAMES)
print someone in usernames

이것에 대한 좋은 점은 클래스 외부의 코드를 변경하지 않고도 많은 개선을위한 길을 열었다는 것입니다. 예를 들어, self.names빠른 조회를 위해 세트를 변경 하거나 (n.lower() for n in self.names)한 번만 계산 하여 클래스에 저장할 수 있습니다.


10

str.casefold대소 문자를 구분하지 않는 문자열 일치에 권장됩니다. @nmichaels의 솔루션 은 사소하게 적용될 수 있습니다.

다음 중 하나를 사용하십시오.

if 'MICHAEL89'.casefold() in (name.casefold() for name in USERNAMES):

또는:

if 'MICHAEL89'.casefold() in map(str.casefold, USERNAMES):

당으로 문서 :

케이스 폴딩은 소문자와 비슷하지만 문자열에서 모든 케이스 구별을 제거하기 때문에 더 공격적입니다. 예를 들어 독일어 소문자 'ß'는 "ss"와 같습니다. 이미 소문자이므로 lower()'ß'에는 아무 것도하지 않습니다. casefold() "ss"로 변환합니다.


8

한 가지 방법이 있습니다.

if string1.lower() in string2.lower(): 
    ...

이것이 작동 하려면 string1string2객체 유형이 모두 이어야합니다 string.


5
AttributeError : 'list'객체에 'lower'속성이 없습니다.
Jeff

@Jeff 그것은 요소 중 하나가 목록이고 두 객체가 모두 문자열이어야하기 때문입니다. 어떤 개체가 목록입니까?
사용자

1
나는 당신을 투표 할 것이지만, 당신이 당신의 답변을 편집하지 않으면 나는 할 수 없습니다. 너가 확실히 맞아.
Jeff

@Jeff 나는 설명을 추가했습니다.
사용자

6

추가 코드를 작성해야한다고 생각합니다. 예를 들면 다음과 같습니다.

if 'MICHAEL89' in map(lambda name: name.upper(), USERNAMES):
   ...

이 경우 모든 항목 USERNAMES을 대문자 로 변환하여 새 목록을 만든 다음이 새 목록과 비교합니다.

최신 정보

으로 @viraptor는 말한다, 대신의 발전기를 사용하는 것이 더 나은 것입니다 map. @Nathon답변을 참조하십시오 .


또는 itertoolsfunction을 사용할 수 있습니다 imap. 발전기보다 훨씬 빠르지 만 동일한 목표를 달성합니다.
wheaties

5

넌 할 수있어

matcher = re.compile('MICHAEL89', re.IGNORECASE)
filter(matcher.match, USERNAMES) 

업데이트 : 조금 놀았고 다음을 사용하여 더 나은 단락 유형 접근법을 얻을 수 있다고 생각합니다

matcher = re.compile('MICHAEL89', re.IGNORECASE)
if any( ifilter( matcher.match, USERNAMES ) ):
    #your code here

ifilter함수는 파이썬에서 내가 가장 좋아하는 모듈 중 하나 인 itertools에서 온 것입니다. 생성기보다 빠르지 만 호출 될 때 목록의 다음 항목 만 작성합니다.


추가하기 위해 패턴은 ".", "?"와 같은 문자를 포함 할 수 있으므로 이스케이프해야 할 수도 있습니다. 정규 표현식 패턴에서 스펙이 의미가 있습니다. re.escape (raw_string)을 사용하여
Iching Chang

0

내 5 (잘못된) 센트

""의 'a'.join (['A ']). lower ()

최신 정보

@, @jpp에 전적으로 동의합니다. 나는 나쁜 습관의 예로 계속 사용할 것입니다 :(


2
이것은 잘못이다. 이것이 OP가 원하지 않는 경우 'a' in "".join(['AB']).lower()반품을 고려하십시오 True.
jpp

0

나는 목록 대신 사전을 위해 이것을 필요로했다 .Jochen 솔루션은 그 경우에 가장 우아했기 때문에 약간 수정했다.

class CaseInsensitiveDict(dict):
    ''' requests special dicts are case insensitive when using the in operator,
     this implements a similar behaviour'''
    def __contains__(self, name): # implements `in`
        return name.casefold() in (n.casefold() for n in self.keys())

지금 당신은 그렇게 사전을 변환 USERNAMESDICT = CaseInsensitiveDict(USERNAMESDICT)하고 사용할 수 있습니다if 'MICHAEL89' in USERNAMESDICT:


0

한 줄에 넣으려면 이것이 내가 한 일입니다.

if any(([True if 'MICHAEL89' in username.upper() else False for username in USERNAMES])):
    print('username exists in list')

나는 그것을 현명하게 테스트하지 않았다. 얼마나 빠르거나 효율적인지 잘 모르겠습니다.

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