Tôi biết cách thực hiện việc này nếu tôi lặp qua tất cả các ký tự trong chuỗi nhưng tôi đang tìm kiếm một phương pháp thanh lịch hơn.
Tôi biết cách thực hiện việc này nếu tôi lặp qua tất cả các ký tự trong chuỗi nhưng tôi đang tìm kiếm một phương pháp thanh lịch hơn.
Câu trả lời:
Một biểu thức chính quy sẽ thực hiện thủ thuật với rất ít mã:
import re
...
if re.match("^[A-Za-z0-9_-]*$", my_little_string):
# do something here
\w
bao gồm \d
và _
do đó isvalid = re.match(r'[\w-]+$', astr)
hoặc isinvalid = re.search(r'[^\w-]', astr)
. Có thể có locale.setlocale
hoặc chuỗi unicode cần phải xem xét thêm.
isvalid = re.match(r'[\w-]*$', astr)
- chuỗi trống là hợp lệ.
[Chỉnh sửa] Có một giải pháp khác chưa được đề cập và nó dường như hoạt động tốt hơn những giải pháp khác được đưa ra cho đến nay trong hầu hết các trường hợp.
Sử dụng string.translate để thay thế tất cả các ký tự hợp lệ trong chuỗi và xem chúng tôi có còn sót lại ký tự không hợp lệ nào không. Quá trình này diễn ra khá nhanh vì nó sử dụng hàm C bên dưới để thực hiện công việc, với rất ít mã bytecode của python tham gia.
Rõ ràng hiệu suất không phải là tất cả - tìm kiếm các giải pháp dễ đọc nhất có lẽ là cách tiếp cận tốt nhất khi không nằm trong một đường dẫn quan trọng về hiệu suất, nhưng chỉ để xem cách các giải pháp xếp chồng lên nhau, đây là so sánh hiệu suất của tất cả các phương pháp được đề xuất cho đến nay. check_trans là một trong những sử dụng phương thức string.translate.
Mã kiểm tra:
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()
Kết quả trên hệ thống của tôi là:
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
Phương pháp dịch có vẻ tốt nhất trong hầu hết các trường hợp, đáng kể như vậy với các chuỗi dài hợp lệ, nhưng bị đánh bại bởi các regex trong test_long_invalid (Có lẽ vì regex có thể cứu trợ ngay lập tức, nhưng dịch luôn phải quét toàn bộ chuỗi). Các phương pháp tiếp cận tập hợp thường kém nhất, chỉ đánh bại regex đối với trường hợp chuỗi rỗng.
Việc sử dụng all (x trong allow_set cho x in s) hoạt động tốt nếu kết quả sớm, nhưng có thể không tốt nếu phải lặp qua mọi ký tự. isSubSet và set chênh lệch có thể so sánh được và luôn tỷ lệ thuận với độ dài của chuỗi bất kể dữ liệu.
Có một sự khác biệt tương tự giữa các phương thức regex khớp với tất cả các ký tự hợp lệ và tìm kiếm các ký tự không hợp lệ. So khớp hoạt động tốt hơn một chút khi kiểm tra một chuỗi dài nhưng hoàn toàn hợp lệ, nhưng tệ hơn đối với các ký tự không hợp lệ ở gần cuối chuỗi.
string.ascii_letters
thay vì string.letters
nếu bạn không sử dụng re.LOCALE cờ cho regexps (nếu không bạn có thể nhận được kết quả dương tính giả trong check_trans()
. string.maketrans()
Sẽ không làm việc cho chuỗi unicode.
from __future__ import unicode_literals
), hãy sử dụng trans_table3 = dict((ord(char), '') for char in allowed_chars)
và định nghĩa check_trans(s): return not s.translate(trans_table3)
. Nhưng nói chung, nó hoạt động kém hơn các phiên bản RE.
Có nhiều cách để đạt được mục tiêu này, một số cách rõ ràng hơn những cách khác. Đối với mỗi ví dụ của tôi, 'True' có nghĩa là chuỗi được truyền vào là hợp lệ, 'False' có nghĩa là nó chứa các ký tự không hợp lệ.
Trước hết, có cách tiếp cận ngây thơ:
import string
allowed = string.letters + string.digits + '_' + '-'
def check_naive(mystring):
return all(c in allowed for c in mystring)
Sau đó, sử dụng một biểu thức chính quy, bạn có thể làm điều này với re.match (). Lưu ý rằng '-' phải ở cuối [] nếu không nó sẽ được sử dụng làm dấu phân cách 'phạm vi'. Cũng lưu ý $ có nghĩa là 'cuối chuỗi'. Các câu trả lời khác được lưu ý trong câu hỏi này sử dụng một lớp ký tự đặc biệt, '\ w', tôi luôn thích sử dụng một dải lớp ký tự rõ ràng bằng cách sử dụng [] vì nó dễ hiểu hơn mà không cần phải tra cứu hướng dẫn tham khảo nhanh và dễ đặc biệt hơn- trường hợp.
import re
CHECK_RE = re.compile('[a-zA-Z0-9_-]+$')
def check_re(mystring):
return CHECK_RE.match(mystring)
Một giải pháp khác lưu ý rằng bạn có thể thực hiện đối sánh nghịch đảo với các biểu thức chính quy, tôi đã đưa điều đó vào đây ngay bây giờ. Lưu ý rằng [^ ...] đảo ngược lớp ký tự vì ^ được sử dụng:
CHECK_INV_RE = re.compile('[^a-zA-Z0-9_-]')
def check_inv_re(mystring):
return not CHECK_INV_RE.search(mystring)
Bạn cũng có thể làm điều gì đó khó khăn với đối tượng 'set'. Hãy xem ví dụ này, ví dụ này sẽ xóa khỏi chuỗi ban đầu tất cả các ký tự được phép, để lại cho chúng tôi một tập hợp chứa a) không có gì hoặc b) các ký tự vi phạm khỏi chuỗi:
def check_set(mystring):
return not set(mystring) - set(allowed)
string.ascii_letters
nếu bạn sử dụng regexps '[a-zA-Z]'.
Nếu không có dấu gạch ngang và dấu gạch dưới, giải pháp đơn giản nhất sẽ là
my_little_string.isalnum()
(Phần 3.6.1 của Tham chiếu Thư viện Python)
Biểu thức chính quy có thể rất linh hoạt.
import re;
re.fullmatch("^[\w-]+$", target_string) # fullmatch looks also workable for python 3.4
\w
: Chỉ có [a-zA-Z0-9_]
Vì vậy, bạn cần thêm ký tự -
cho dấu gạch ngang căn lề.
+
: Khớp một hoặc nhiều lần lặp lại ký tự trước đó. Tôi đoán bạn không chấp nhận đầu vào trống. Nhưng nếu bạn làm vậy, hãy thay đổi thành *
.
^
: Khớp với phần bắt đầu của chuỗi.
$
: Khớp với phần cuối của chuỗi.
Bạn cần hai ký tự đặc biệt này vì bạn cần tránh trường hợp sau. Các ký tự không mong muốn như &
ở đây có thể xuất hiện giữa các mẫu phù hợp.
&&&PATTERN&&PATTERN
Vâng, bạn có thể yêu cầu sự giúp đỡ của regex, tuyệt vời ở đây :)
mã:
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'
Đầu ra:
yes
Hi vọng điêu nay co ich :)
Bạn luôn có thể sử dụng toàn bộ danh sách và kiểm tra kết quả, sẽ ít tốn tài nguyên hơn một chút so với sử dụng regex: all([c in string.letters + string.digits + ["_", "-"] for c in mystring])
Đây là điều gì đó dựa trên "cách tiếp cận ngây thơ" của Jerub (ngây thơ là lời của anh ấy, không phải của tôi!):
import string
ALLOWED = frozenset(string.ascii_letters + string.digits + '_' + '-')
def check(mystring):
return all(c in ALLOWED for c in mystring)
Nếu ALLOWED
là một chuỗi thì tôi nghĩ c in ALLOWED
sẽ liên quan đến việc lặp lại từng ký tự trong chuỗi cho đến khi nó tìm thấy khớp hoặc đến cuối. Mà, để trích dẫn Joel Spolsky, là một cái gì đó của thuật toán Shlemiel the Painter .
Nhưng việc kiểm tra sự tồn tại trong một tập hợp sẽ hiệu quả hơn, hoặc ít nhất là ít phụ thuộc hơn vào số lượng ký tự được phép. Chắc chắn cách tiếp cận này nhanh hơn một chút trên máy của tôi. Nó rõ ràng và tôi nghĩ rằng nó hoạt động đủ tốt cho hầu hết các trường hợp (trên máy chậm của tôi, tôi có thể xác nhận hàng chục nghìn chuỗi ish ngắn trong một phần của giây). Tôi thích nó.
THỰC TẾ trên máy tính của tôi, regexp hoạt động nhanh hơn nhiều lần và đơn giản như thế này (được cho là đơn giản hơn). Vì vậy, đó có lẽ là cách tốt nhất về phía trước.
sử dụng regex và xem nó có khớp không!
([a-z][A-Z][0-9]\_\-)*