ฉันรู้วิธีการทำเช่นนี้หากฉันวนซ้ำตัวอักษรทั้งหมดในสตริง แต่ฉันกำลังมองหาวิธีที่หรูหรากว่านี้
ฉันรู้วิธีการทำเช่นนี้หากฉันวนซ้ำตัวอักษรทั้งหมดในสตริง แต่ฉันกำลังมองหาวิธีที่หรูหรากว่านี้
คำตอบ:
นิพจน์ทั่วไปจะทำเคล็ดลับโดยใช้โค้ดน้อยมาก:
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
สตริงหรือ Unicode ที่เป็นไปได้จำเป็นต้องพิจารณาเพิ่มเติม
isvalid = re.match(r'[\w-]*$', astr)
- สตริงว่างถูกต้อง
[แก้ไข] ยังไม่มีวิธีแก้ปัญหาอื่นที่ยังไม่ได้กล่าวถึงและดูเหมือนว่าจะมีประสิทธิภาพดีกว่าวิธีอื่น ๆ ที่ให้ไว้ในกรณีส่วนใหญ่
ใช้ string.translate เพื่อแทนที่อักขระที่ถูกต้องทั้งหมดในสตริงและดูว่าเรามีอักขระที่ไม่ถูกต้องเหลืออยู่หรือไม่ สิ่งนี้ค่อนข้างเร็วเนื่องจากใช้ฟังก์ชัน C พื้นฐานในการทำงานโดยมีไบโทโค้ด python น้อยมากที่เกี่ยวข้อง
เห็นได้ชัดว่าประสิทธิภาพไม่ใช่ทุกอย่างการไปหาโซลูชันที่อ่านได้มากที่สุดน่าจะเป็นแนวทางที่ดีที่สุดเมื่อไม่ได้อยู่ใน codepath ที่สำคัญด้านประสิทธิภาพ แต่เพียงเพื่อดูว่าโซลูชันซ้อนกันอย่างไรนี่คือการเปรียบเทียบประสิทธิภาพของวิธีการทั้งหมดที่เสนอจนถึงตอนนี้ 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
วิธีการแปลดูเหมือนจะดีที่สุดในกรณีส่วนใหญ่เป็นอย่างมากดังนั้นด้วยสตริงที่ยาวใช้ได้ แต่ถูกตีกลับโดย regexes ใน test_long_invalid (น่าจะเป็นเพราะ regex สามารถประกันตัวได้ทันที แต่การแปลจะต้องสแกนทั้งสตริงเสมอ) วิธีการ set มักจะแย่ที่สุดโดยจะตี regexes สำหรับสตริงว่างเท่านั้น
การใช้ all (x ใน allow_set สำหรับ x in s) จะทำงานได้ดีถ้ามันออกมาก่อนเวลา แต่อาจไม่ดีหากต้องวนซ้ำทุกตัวอักษร isSubSet และความแตกต่างของชุดสามารถเปรียบเทียบได้และเป็นสัดส่วนที่สม่ำเสมอกับความยาวของสตริงโดยไม่คำนึงถึงข้อมูล
มีความแตกต่างที่คล้ายกันระหว่างเมธอด regex ที่ตรงกับอักขระที่ถูกต้องทั้งหมดและค้นหาอักขระที่ไม่ถูกต้อง การจับคู่จะทำงานได้ดีขึ้นเล็กน้อยเมื่อตรวจสอบสตริงที่ยาว แต่ใช้ได้อย่างสมบูรณ์ แต่แย่กว่าสำหรับอักขระที่ไม่ถูกต้องใกล้กับส่วนท้ายของสตริง
string.ascii_letters
แทนstring.letters
ถ้าคุณไม่ได้ใช้ธง re.LOCALE สำหรับ regexps (มิฉะนั้นคุณอาจได้รับผลบวกปลอมในcheck_trans()
. string.maketrans()
จะไม่ทำงานสำหรับสตริง Unicode.
from __future__ import unicode_literals
), การใช้งานtrans_table3 = dict((ord(char), '') for char in allowed_chars)
และ check_trans(s): return not s.translate(trans_table3)
def แต่โดยทั่วไปแล้วจะทำงานได้แย่กว่ารุ่น 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 () โปรดทราบว่า "-" จะต้องอยู่ท้าย [] มิฉะนั้นจะใช้เป็นตัวคั่น "ช่วง" โปรดสังเกตว่า $ ซึ่งหมายถึง 'end of string' คำตอบอื่น ๆ ที่ระบุไว้ในคำถามนี้ใช้คลาสอักขระพิเศษ '\ 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) nothing หรือ b) อักขระที่ละเมิดออกจากสตริง:
def check_set(mystring):
return not set(mystring) - set(allowed)
string.ascii_letters
ถ้าคุณใช้ regexps '[a-zA-Z]'
หากไม่ใช่สำหรับขีดกลางและขีดล่างวิธีแก้ปัญหาที่ง่ายที่สุดคือ
my_little_string.isalnum()
(ส่วน3.6.1ของการอ้างอิงไลบรารี Python)
อีกทางเลือกหนึ่งนอกเหนือจากการใช้ regex คุณสามารถทำได้ในชุด:
from sets import Set
allowed_chars = Set('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-')
if Set(my_little_sting).issubset(allowed_chars):
# your action
print True
pat = re.compile ('[^\w-]')
def onlyallowed(s):
return not pat.search (s)
นิพจน์ทั่วไปสามารถยืดหยุ่นได้มาก
import re;
re.fullmatch("^[\w-]+$", target_string) # fullmatch looks also workable for python 3.4
\w
: เท่านั้น [a-zA-Z0-9_]
ดังนั้นคุณต้องเพิ่ม-
char เพื่อปรับค่าขีดกลาง char
+
: จับคู่การทำซ้ำอย่างน้อยหนึ่งครั้งของอักขระก่อนหน้า ฉันเดาว่าคุณไม่ยอมรับการป้อนข้อมูลเปล่า *
แต่ถ้าคุณทำเปลี่ยนไป
^
: ตรงกับจุดเริ่มต้นของสตริง
$
: ตรงกับจุดสิ้นสุดของสตริง
คุณต้องใช้อักขระพิเศษสองตัวนี้เนื่องจากคุณต้องหลีกเลี่ยงกรณีต่อไปนี้ ตัวอักษรที่ไม่ต้องการเช่น&
นี้อาจปรากฏขึ้นระหว่างรูปแบบที่ตรงกัน
&&&PATTERN&&PATTERN
คุณสามารถขอความช่วยเหลือจาก regex ได้ที่นี่ :)
รหัส:
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
หวังว่านี่จะช่วยได้ :)
คุณสามารถใช้การทำความเข้าใจรายการและตรวจสอบผลลัพธ์ได้ตลอดเวลามันจะใช้ทรัพยากรน้อยกว่าการใช้ regex เล็กน้อย: 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
จะเกี่ยวข้องกับการวนซ้ำอักขระแต่ละตัวในสตริงจนกว่าจะพบรายการที่ตรงกันหรือถึงจุดสิ้นสุด ซึ่งจะพูดโจ Spolsky เป็นอะไรบางอย่างที่Shlemiel อัลกอริทึมจิตรกร
แต่การทดสอบการมีอยู่ในชุดควรมีประสิทธิภาพมากกว่าหรืออย่างน้อยก็น้อยขึ้นอยู่กับจำนวนอักขระที่อนุญาต แน่นอนว่าวิธีนี้เร็วกว่าเล็กน้อยในเครื่องของฉัน ชัดเจนและฉันคิดว่ามันทำงานได้ดีพอสำหรับกรณีส่วนใหญ่ (ในเครื่องที่ช้าฉันสามารถตรวจสอบสตริง short-ish นับหมื่นได้ในเสี้ยววินาที) ฉันชอบมัน.
จริงๆแล้ว regexp บนเครื่องของฉันทำงานได้เร็วขึ้นหลายเท่าและทำได้ง่ายเพียงเท่านี้ (เนื้อหาง่ายกว่า) นั่นอาจเป็นวิธีที่ดีที่สุดในอนาคต
ใช้ regex แล้วดูว่าตรงกันไหม!
([a-z][A-Z][0-9]\_\-)*