Dizedeki tüm karakterleri yinelediğimde bunu nasıl yapacağımı biliyorum ama daha zarif bir yöntem arıyorum.
Dizedeki tüm karakterleri yinelediğimde bunu nasıl yapacağımı biliyorum ama daha zarif bir yöntem arıyorum.
Yanıtlar:
Normal bir ifade, çok az kodla hile yapacaktır:
import re
...
if re.match("^[A-Za-z0-9_-]*$", my_little_string):
# do something here
\wiçerir \dve _bu nedenle isvalid = re.match(r'[\w-]+$', astr)veya isinvalid = re.search(r'[^\w-]', astr). Olası bir locale.setlocaleunicode dizesi varlığı, ek değerlendirme gerektirir.
isvalid = re.match(r'[\w-]*$', astr)- boş dizeler geçerlidir.
[Düzenle] Henüz bahsedilmeyen başka bir çözüm var ve çoğu durumda şimdiye kadar verilen diğerlerinden daha iyi performans gösteriyor gibi görünüyor.
Dizedeki tüm geçerli karakterleri değiştirmek için string.translate kullanın ve geriye kalan geçersiz karakterler olup olmadığına bakın. Çok az python bayt kodu dahil olmak üzere, işi yapmak için temeldeki C işlevini kullandığı için bu oldukça hızlıdır.
Açıkçası performans her şey değildir - en okunabilir çözümlere gitmek, performans açısından kritik bir kod yolunda olmadığında muhtemelen en iyi yaklaşımdır, ancak çözümlerin nasıl yığıldığını görmek için, şimdiye kadar önerilen tüm yöntemlerin performans karşılaştırmasını burada bulabilirsiniz. check_trans, string.translate yöntemini kullanan olandır.
Test kodu:
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()
Sistemimdeki sonuçlar:
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
Çeviri yaklaşımı çoğu durumda en iyi gibi görünür, uzun geçerli dizelerde dramatik olarak böyledir, ancak test_long_invalid'deki normal ifadeler tarafından dövülür (Muhtemelen normal ifadenin hemen kurtarılabilmesi, ancak çevirinin her zaman tüm dizeyi taraması gerektiğinden). Küme yaklaşımları genellikle en kötüsüdür, normal ifadeleri yalnızca boş dize durumu için yener.
Tümünü (x in allow_set for x in s) kullanmak, erken çıkarsa iyi sonuç verir, ancak her karakteri yinelemesi gerekiyorsa kötü olabilir. isSubSet ve küme farkı karşılaştırılabilir ve verilerden bağımsız olarak dizenin uzunluğuyla tutarlı bir şekilde orantılıdır.
Tüm geçerli karakterlerle eşleşen ve geçersiz karakterleri arama normal ifade yöntemleri arasında benzer bir fark vardır. Eşleştirme, uzun, ancak tamamen geçerli bir dizeyi kontrol ederken biraz daha iyi performans gösterir, ancak dizenin sonuna yakın geçersiz karakterler için daha kötüdür.
string.ascii_lettersbunun yerine kullanın string.letters(aksi takdirde içinde yanlış pozitif sonuçlar alabilirsiniz check_trans(). string.maketrans()Unicode dizeleri için çalışmayacaktır.
from __future__ import unicode_literals) için kullanın trans_table3 = dict((ord(char), '') for char in allowed_chars)ve tanımlayın check_trans(s): return not s.translate(trans_table3). Ancak genel olarak RE versiyonlarından daha kötü performans gösterir.
Bu hedefe ulaşmanın çeşitli yolları vardır, bazıları diğerlerinden daha nettir. Örneklerimin her biri için 'True', aktarılan dizenin geçerli olduğu, 'False' ise geçersiz karakterler içerdiği anlamına gelir.
Her şeyden önce saf bir yaklaşım var:
import string
allowed = string.letters + string.digits + '_' + '-'
def check_naive(mystring):
return all(c in allowed for c in mystring)
Sonra bir normal ifade kullanımı var, bunu re.match () ile yapabilirsiniz. '-' öğesinin [] işaretinin sonunda olması gerektiğine dikkat edin, aksi takdirde bir 'aralık' sınırlayıcı olarak kullanılacaktır. Ayrıca 'dizgenin sonu' anlamına gelen $ 'a da dikkat edin. Bu soruda belirtilen diğer yanıtlar özel bir karakter sınıfı, '\ w' kullanır, ben her zaman [] kullanarak açık bir karakter sınıfı aralığı kullanmayı tercih ederim çünkü hızlı bir başvuru kılavuzuna bakmak zorunda kalmadan anlamak daha kolaydır ve özelden daha kolaydır durum.
import re
CHECK_RE = re.compile('[a-zA-Z0-9_-]+$')
def check_re(mystring):
return CHECK_RE.match(mystring)
Başka bir çözüm, normal ifadelerle ters eşleşme yapabileceğinizi belirtti, bunu şimdi buraya ekledim. [^ ...] karakter sınıfını ters çevirir çünkü ^ kullanılır:
CHECK_INV_RE = re.compile('[^a-zA-Z0-9_-]')
def check_inv_re(mystring):
return not CHECK_INV_RE.search(mystring)
Ayrıca 'set' nesnesiyle zor bir şey de yapabilirsiniz. Orijinal dizeden izin verilen tüm karakterleri kaldıran ve bize a) hiçbir şey içermeyen veya b) dizeden rahatsız edici karakterleri içeren bir küme bırakan bu örneğe bir göz atın:
def check_set(mystring):
return not set(mystring) - set(allowed)
string.ascii_letterssen '[a-zA-Z]' İfadelerinin kullanırsanız.
Kısa çizgiler ve alt çizgiler olmasaydı, en kolay çözüm olurdu
my_little_string.isalnum()
( Python Kitaplığı Referansı Bölüm 3.6.1 )
Düzenli ifade çok esnek olabilir.
import re;
re.fullmatch("^[\w-]+$", target_string) # fullmatch looks also workable for python 3.4
\w: Bir tek [a-zA-Z0-9_]
Bu nedenle, -kısa çizgi karakterini yaslamak için karakter eklemeniz gerekir .
+: Önceki karakterin bir veya daha fazla tekrarını eşleştirin. Sanırım boş girişi kabul etmiyorsunuz. Ama yaparsan, olarak değiştir *.
^: Dizenin başlangıcıyla eşleşir.
$: Dizenin sonuyla eşleşir.
Aşağıdaki durumdan kaçınmanız gerektiğinden bu iki özel karaktere ihtiyacınız var. Buradaki gibi istenmeyen karakterler &, eşleşen desen arasında görünebilir.
&&&PATTERN&&PATTERN
Pekala, regex'in yardımını isteyebilirsin, buradaki harika :)
kod:
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'
Çıktı:
yes
Bu yardımcı olur umarım :)
Her zaman bir liste anlama kullanabilir ve sonuçları hepsiyle kontrol edebilirsiniz, bir normal ifade kullanmaktan biraz daha az kaynak yoğun olacaktır: all([c in string.letters + string.digits + ["_", "-"] for c in mystring])
İşte Jerub'un "saf yaklaşımı" na dayanan bir şey (onun sözleri saf, benim değil!):
import string
ALLOWED = frozenset(string.ascii_letters + string.digits + '_' + '-')
def check(mystring):
return all(c in ALLOWED for c in mystring)
Eğer ALLOWEDbir dize oldu sonra düşünüyorum c in ALLOWEDda bir maç bulunamadı veya sonuna ulaşana kadar dizedeki her karakterin üzerinde iterating yer alacak. Joel Spolsky'den alıntı yapacak olursak, Shlemiel the Painter algoritması gibi .
Ancak bir kümede var olup olmadığını test etmek daha verimli olmalı veya en azından izin verilen karakter sayısına daha az bağlı olmalıdır. Elbette bu yaklaşım benim makinemde biraz daha hızlı. Açıktır ve çoğu durum için yeterince iyi performans gösterdiğini düşünüyorum (yavaş makinemde onbinlerce kısa telli dizgiyi bir saniyeden kısa sürede doğrulayabilirim). Bunu sevdim.
GERÇEKTEN , makinemde bir regexp birkaç kat daha hızlı çalışıyor ve bu kadar basit (muhtemelen daha basit). Yani bu muhtemelen ilerlemenin en iyi yoludur.
bir normal ifade kullanın ve eşleşip eşleşmediğine bakın!
([a-z][A-Z][0-9]\_\-)*