문자열의 모든 문자를 반복하면이 작업을 수행하는 방법을 알고 있지만 더 우아한 방법을 찾고 있습니다.
답변:
정규 표현식은 아주 적은 코드로 트릭을 수행합니다.
import re
...
if re.match("^[A-Za-z0-9_-]*$", my_little_string):
# do something here
\w
포함 \d
하고 _
, 따라서 isvalid = re.match(r'[\w-]+$', astr)
나 isinvalid = re.search(r'[^\w-]', astr)
. 의 존재 가능성 locale.setlocale
또는 유니 코드 문자열은 추가적인 고려가 필요합니다.
isvalid = re.match(r'[\w-]*$', astr)
-빈 문자열이 유효합니다.
[편집] 아직 언급되지 않은 또 다른 솔루션이 있으며, 대부분의 경우 지금까지 제공된 다른 솔루션을 능가하는 것으로 보입니다.
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 및 집합 차이는 비교할 수 있으며 데이터에 관계없이 문자열 길이에 일관되게 비례합니다.
모든 유효한 문자와 일치하는 정규식 메소드와 유효하지 않은 문자를 검색하는 것 사이에는 비슷한 차이가 있습니다. 일치는 길지만 완전히 유효한 문자열을 검사 할 때 약간 더 잘 수행되지만 문자열 끝 부분에있는 유효하지 않은 문자의 경우 더 나쁩니다.
string.ascii_letters
대신 사용하십시오 string.letters
(그렇지 않으면 check_trans()
. 에서 거짓 긍정 결과를 얻을 수 있습니다 . string.maketrans()
유니 코드 문자열에는 작동하지 않습니다.
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 버전보다 성능이 떨어집니다.
이 목표를 달성하는 방법에는 여러 가지가 있으며 일부는 다른 것보다 명확합니다. 각 예제에서 '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)
string.ascii_letters
'[a-zA-Z]'정규식을 사용하는 경우 사용 합니다.
정규 표현식은 매우 유연 할 수 있습니다.
import re;
re.fullmatch("^[\w-]+$", target_string) # fullmatch looks also workable for python 3.4
\w
: 뿐 [a-zA-Z0-9_]
따라서 -
하이픈 문자를 정당화 하려면 문자 를 추가해야합니다 .
+
: 선행 문자의 하나 이상의 반복과 일치합니다. 빈 입력을 받아들이지 않는 것 같습니다. 그러나 그렇게한다면 *
.
^
: 문자열의 시작과 일치합니다.
$
: 문자열의 끝과 일치합니다.
다음 대소 문자를 피해야하므로이 두 특수 문자가 필요합니다. &
여기 와 같이 원하지 않는 문자 가 일치하는 패턴 사이에 나타날 수 있습니다.
&&&PATTERN&&PATTERN
그럼 정규식의 도움을 요청할 수 있습니다.
암호:
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
도움이 되었기를 바랍니다 :)
항상 목록 이해력을 사용하고 결과를 모두 확인할 수 있습니다. 정규식을 사용하는 것보다 리소스 집약적입니다. all([c in string.letters + string.digits + ["_", "-"] for c in mystring])
다음은 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 알고리즘의 일부 입니다.
그러나 세트에 존재하는지 테스트하는 것이 더 효율적이거나 적어도 허용되는 문자 수에 따라 덜 의존해야합니다. 확실히이 접근 방식은 내 컴퓨터에서 조금 더 빠릅니다. 명확하고 대부분의 경우에 충분히 잘 수행된다고 생각합니다 (저의 느린 컴퓨터에서는 몇 분 안에 수만 개의 짧은 문자열을 확인할 수 있습니다). 나는 그것을 좋아한다.
실제로 내 컴퓨터에서 정규 표현식은 몇 배 더 빠르게 작동하며 이것만큼 간단합니다 (아마도 더 간단합니다). 이것이 아마도 최선의 방법 일 것입니다.