문자열에 문자, 숫자, 밑줄 및 대시 만 포함되어 있는지 확인하려면 어떻게합니까?


87

문자열의 모든 문자를 반복하면이 작업을 수행하는 방법을 알고 있지만 더 우아한 방법을 찾고 있습니다.


5
ascii, 로케일 특정 또는 유니 코드 문자에 대해 이야기하고 있습니까?
jfs

답변:


124

정규 표현식은 아주 적은 코드로 트릭을 수행합니다.

import re

...

if re.match("^[A-Za-z0-9_-]*$", my_little_string):
    # do something here

25
다음과 같이 단순화 할 수 있습니다. ^ [\ w \ d _-] * $
Prestaul

13
이 솔루션은 길이가 0 인 문자열과 일치합니다. * 대신 +를 사용하여 1 자 이상의 문자열과 일치 시키십시오.
Jerub

10
@Prestaul : \w포함 \d하고 _, 따라서 isvalid = re.match(r'[\w-]+$', astr)isinvalid = re.search(r'[^\w-]', astr). 의 존재 가능성 locale.setlocale또는 유니 코드 문자열은 추가적인 고려가 필요합니다.
jfs

1
수정 : isvalid = re.match(r'[\w-]*$', astr)-빈 문자열이 유효합니다.
jfs

해당 정규식에서 마침표 / 점 (.)을 어떻게 허용 할 수 있습니까? 편집, 방법은 다음과 같습니다. ^ [a-zA-Z0-9 -_ \ s \.] + $
fredrik

24

[편집] 아직 언급되지 않은 또 다른 솔루션이 있으며, 대부분의 경우 지금까지 제공된 다른 솔루션을 능가하는 것으로 보입니다.

string.translate를 사용하여 문자열의 모든 유효한 문자를 교체하고 유효하지 않은 문자가 남아 있는지 확인하십시오. 이것은 작업을 수행하기 위해 기본 C 함수를 사용하므로 매우 빠르며 Python 바이트 코드가 거의 포함되지 않습니다.

분명히 성능이 전부는 아닙니다. 성능이 중요한 코드 경로에 있지 않을 때 가장 가독성이 좋은 솔루션을 찾는 것이 아마도 최상의 접근 방식 일 것입니다.하지만 솔루션이 어떻게 쌓이는 지보기 위해 지금까지 제안 된 모든 방법의 성능 비교가 있습니다. check_trans는 string.translate 메소드를 사용하는 것입니다.

테스트 코드 :

import string, re, timeit

pat = re.compile('[\w-]*$')
pat_inv = re.compile ('[^\w-]')
allowed_chars=string.ascii_letters + string.digits + '_-'
allowed_set = set(allowed_chars)
trans_table = string.maketrans('','')

def check_set_diff(s):
    return not set(s) - allowed_set

def check_set_all(s):
    return all(x in allowed_set for x in s)

def check_set_subset(s):
    return set(s).issubset(allowed_set)

def check_re_match(s):
    return pat.match(s)

def check_re_inverse(s): # Search for non-matching character.
    return not pat_inv.search(s)

def check_trans(s):
    return not s.translate(trans_table,allowed_chars)

test_long_almost_valid='a_very_long_string_that_is_mostly_valid_except_for_last_char'*99 + '!'
test_long_valid='a_very_long_string_that_is_completely_valid_' * 99
test_short_valid='short_valid_string'
test_short_invalid='/$%$%&'
test_long_invalid='/$%$%&' * 99
test_empty=''

def main():
    funcs = sorted(f for f in globals() if f.startswith('check_'))
    tests = sorted(f for f in globals() if f.startswith('test_'))
    for test in tests:
        print "Test %-15s (length = %d):" % (test, len(globals()[test]))
        for func in funcs:
            print "  %-20s : %.3f" % (func, 
                   timeit.Timer('%s(%s)' % (func, test), 'from __main__ import pat,allowed_set,%s' % ','.join(funcs+tests)).timeit(10000))
        print

if __name__=='__main__': main()

내 시스템의 결과는 다음과 같습니다.

Test test_empty      (length = 0):
  check_re_inverse     : 0.042
  check_re_match       : 0.030
  check_set_all        : 0.027
  check_set_diff       : 0.029
  check_set_subset     : 0.029
  check_trans          : 0.014

Test test_long_almost_valid (length = 5941):
  check_re_inverse     : 2.690
  check_re_match       : 3.037
  check_set_all        : 18.860
  check_set_diff       : 2.905
  check_set_subset     : 2.903
  check_trans          : 0.182

Test test_long_invalid (length = 594):
  check_re_inverse     : 0.017
  check_re_match       : 0.015
  check_set_all        : 0.044
  check_set_diff       : 0.311
  check_set_subset     : 0.308
  check_trans          : 0.034

Test test_long_valid (length = 4356):
  check_re_inverse     : 1.890
  check_re_match       : 1.010
  check_set_all        : 14.411
  check_set_diff       : 2.101
  check_set_subset     : 2.333
  check_trans          : 0.140

Test test_short_invalid (length = 6):
  check_re_inverse     : 0.017
  check_re_match       : 0.019
  check_set_all        : 0.044
  check_set_diff       : 0.032
  check_set_subset     : 0.037
  check_trans          : 0.015

Test test_short_valid (length = 18):
  check_re_inverse     : 0.125
  check_re_match       : 0.066
  check_set_all        : 0.104
  check_set_diff       : 0.051
  check_set_subset     : 0.046
  check_trans          : 0.017

번역 접근 방식은 대부분의 경우 가장 좋은 것처럼 보이며, 유효한 긴 문자열을 사용하면 극적으로 길지만 test_long_invalid의 정규식에 의해 패배합니다 (아마도 정규식이 즉시 중단 될 수 있지만 번역은 항상 전체 문자열을 스캔해야하기 때문입니다). 설정된 접근 방식은 일반적으로 최악이며 빈 문자열 케이스에 대해서만 정규식을 이깁니다.

all (x in s에 대해 allowed_set에서 x)을 사용하면 조기에 구제되면 잘 수행되지만 모든 문자를 반복해야하는 경우에는 좋지 않을 수 있습니다. isSubSet 및 집합 차이는 비교할 수 있으며 데이터에 관계없이 문자열 길이에 일관되게 비례합니다.

모든 유효한 문자와 일치하는 정규식 메소드와 유효하지 않은 문자를 검색하는 것 사이에는 비슷한 차이가 있습니다. 일치는 길지만 완전히 유효한 문자열을 검사 할 때 약간 더 잘 수행되지만 문자열 끝 부분에있는 유효하지 않은 문자의 경우 더 나쁩니다.


1
regexps에 re.LOCALE 플래그를 사용하지 않는 경우 string.ascii_letters대신 사용하십시오 string.letters(그렇지 않으면 check_trans(). 에서 거짓 긍정 결과를 얻을 수 있습니다 . string.maketrans()유니 코드 문자열에는 작동하지 않습니다.
jfs

Python 3 / Unicode / from __future__ import unicode_literals)의 경우 trans_table3 = dict((ord(char), '') for char in allowed_chars)및 def를 사용하십시오 check_trans(s): return not s.translate(trans_table3). 그러나 일반적으로 RE 버전보다 성능이 떨어집니다.
Hugo

15

이 목표를 달성하는 방법에는 여러 가지가 있으며 일부는 다른 것보다 명확합니다. 각 예제에서 'True'는 전달 된 문자열이 유효 함을 의미하고 'False'는 잘못된 문자가 포함되어 있음을 의미합니다.

우선, 순진한 접근 방식이 있습니다.

import string
allowed = string.letters + string.digits + '_' + '-'

def check_naive(mystring):
    return all(c in allowed for c in mystring)

그런 다음 정규 표현식을 사용하면 re.match ()로이를 수행 할 수 있습니다. '-'는 [] 끝에 있어야합니다. 그렇지 않으면 '범위'구분 기호로 사용됩니다. 또한 '문자열의 끝'을 의미하는 $에 유의하십시오. 이 질문에 언급 된 다른 답변은 특수 문자 클래스 '\ w'를 사용합니다. 빠른 참조 가이드를 찾아 볼 필요없이 이해하기 쉽고 특수 문자 클래스가 더 쉽기 때문에 항상 []를 사용하여 명시적인 문자 클래스 범위를 사용하는 것을 선호합니다. 케이스.

import re
CHECK_RE = re.compile('[a-zA-Z0-9_-]+$')
def check_re(mystring):
    return CHECK_RE.match(mystring)

또 다른 솔루션은 정규 표현식과 역 일치를 수행 할 수 있다는 점을 지적했습니다. 지금 여기에 포함 시켰습니다. ^가 사용되기 때문에 [^ ...]는 문자 클래스를 반전시킵니다.

CHECK_INV_RE = re.compile('[^a-zA-Z0-9_-]')
def check_inv_re(mystring):
   return not CHECK_INV_RE.search(mystring)

'set'개체로 까다로운 작업을 수행 할 수도 있습니다. 허용되는 모든 문자를 원래 문자열에서 제거하고 a) 아무것도 포함하지 않거나 b) 문자열에서 문제가되는 문자를 포함하는 집합을 남겨 두는이 예제를 살펴보십시오.

def check_set(mystring):
    return not set(mystring) - set(allowed)

첫 번째 정규식 테스트에서 "[a-zA-Z0-9 _-] + $"는 "[a-zA-Z0-9 _-] * $"가 아니어야합니다. 빈 문자열은 일치하는 것으로 간주되어야합니다.
Brian

string.ascii_letters'[a-zA-Z]'정규식을 사용하는 경우 사용 합니다.
jfs


4

정규식을 사용하는 대신 세트에서 수행 할 수 있습니다.

from sets import Set

allowed_chars = Set('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-')

if Set(my_little_sting).issubset(allowed_chars):
    # your action
    print True

3
 pat = re.compile ('[^\w-]')

 def onlyallowed(s):
    return not pat.search (s)

1

정규 표현식은 매우 유연 할 수 있습니다.

import re;
re.fullmatch("^[\w-]+$", target_string) # fullmatch looks also workable for python 3.4

\w: 뿐 [a-zA-Z0-9_]

따라서 -하이픈 문자를 정당화 하려면 문자 를 추가해야합니다 .

+: 선행 문자의 하나 이상의 반복과 일치합니다. 빈 입력을 받아들이지 않는 것 같습니다. 그러나 그렇게한다면 *.

^: 문자열의 시작과 일치합니다.

$: 문자열의 끝과 일치합니다.

다음 대소 문자를 피해야하므로이 두 특수 문자가 필요합니다. &여기 와 같이 원하지 않는 문자 가 일치하는 패턴 사이에 나타날 수 있습니다.

&&&PATTERN&&PATTERN


0

그럼 정규식의 도움을 요청할 수 있습니다.

암호:

import re

string = 'adsfg34wrtwe4r2_()' #your string that needs to be matched.
regex = r'^[\w\d_()]*$' # you can also add a space in regex if u want to allow it in the string  
if re.match(regex,string):
    print 'yes'
else: 
    print 'false'

산출:

yes  

도움이 되었기를 바랍니다 :)


-1

항상 목록 이해력을 사용하고 결과를 모두 확인할 수 있습니다. 정규식을 사용하는 것보다 리소스 집약적입니다. all([c in string.letters + string.digits + ["_", "-"] for c in mystring])


게시하기 전에 코드를 테스트하십시오. 실행되는 깨진 대답을 기반으로 한 솔루션은 다음과 같습니다. all (c in string.letters + string.digits + "_"for c in mystring)
Jerub

2
그것은 정규식보다 훨씬 더 많은 리소스 집약적 일 것입니다. 모든 캐릭터에 대해 선형 스캔을 수행하고 (미리 세트를 구축하는 것이 더 좋음) 생성기 이해도가 더 가벼울 때 불필요하게 목록을 구축합니다.
Brian

-1

다음은 Jerub의 "순진한 접근 방식"에 기반한 것입니다 (내가 아니라 그의 말이 순진합니다!).

import string
ALLOWED = frozenset(string.ascii_letters + string.digits + '_' + '-')

def check(mystring):
    return all(c in ALLOWED for c in mystring)

경우 ALLOWED문자열 한 후 생각 c in ALLOWED이 일치하는 항목을 찾을 수 없거나 끝에 도달 할 때까지 문자열의 각 문자를 통해 반복하는을 포함한다. Joel Spolsky를 인용하자면 Shlemiel the Painter 알고리즘의 일부 입니다.

그러나 세트에 존재하는지 테스트하는 것이 더 효율적이거나 적어도 허용되는 문자 수에 따라 덜 의존해야합니다. 확실히이 접근 방식은 내 컴퓨터에서 조금 더 빠릅니다. 명확하고 대부분의 경우에 충분히 잘 수행된다고 생각합니다 (저의 느린 컴퓨터에서는 몇 분 안에 수만 개의 짧은 문자열을 확인할 수 있습니다). 나는 그것을 좋아한다.

실제로 내 컴퓨터에서 정규 표현식은 몇 배 더 빠르게 작동하며 이것만큼 간단합니다 (아마도 더 간단합니다). 이것이 아마도 최선의 방법 일 것입니다.


-4

정규식을 사용하고 일치하는지 확인하십시오!

([a-z][A-Z][0-9]\_\-)*

1
이 모든 문자는 하나의 클래스에 있어야합니다. 그렇지 않으면 거짓 부정이 발생합니다. 또한 문자열 시작 및 문자열 끝 마커를 포함하는 것을 잊었습니다 ... 이와 같이 유효한 문자가 하나만있는 한 항상 일치합니다.
Thomas

1
유효한 문자가 없더라도 실제로 일치합니다. 길이가 0입니다. 또한 파이썬이 아닙니다.
Jerub
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.