it-swarm-tr.com

Bir dizenin yalnızca harfler, sayılar, alt çizgiler ve kısa çizgiler içerdiğini nasıl doğrularım?

Dizedeki tüm karakterleri yinelersem bunun nasıl yapıldığını biliyorum ama daha şık bir yöntem arıyorum.

76
Ethan Post

Düzenli bir ifade çok az kod ile hile yapacak:

import re

...

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

[Düzenle] Henüz belirtilmeyen başka bir çözüm var ve çoğu durumda şu ana kadar verilmiş olanlardan daha iyi görünüyor.

Dize içindeki tüm geçerli karakterleri değiştirmek için string.translate komutunu kullanın ve geride geçersiz karakterler olup olmadığına bakın. Çok az python bytecode ile çalışmak için, temel C işlevini kullandığından, bu oldukça hızlı.

Açıkçası, performans her şey değildir - en okunaklı çözümlere gitmek, performans açısından kritik bir kodepatta olmasa da en iyi yaklaşımdır, ancak yalnızca çözümlerin nasıl biriktiğini görmek için, işte şimdiye kadar önerilen tüm yöntemlerin performans karşılaştırması. check_trans string.translate metodunu kullanandı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

Tercümanlık yaklaşımı çoğu durumda en iyi şekilde görünür, dramatik şekilde uzun geçerli dizelerle, ancak test_long_invalid içindeki regex'ler tarafından dövülür (Muhtemelen regex'in hemen kaybedebileceği, ancak çeviri her zaman tüm dizeyi taraması gerektiği için). Ayarlanan yaklaşımlar genellikle en kötü olanıdır, yalnızca boş string case için regex'ler atar.

All (x in s için x in allow_set) komutunun kullanılması, erken ayrılırsa iyi performans gösterir, ancak her karakterde yinelenmek zorunda kalırsa kötü olabilir. isSubSet ve ayarlanan fark karşılaştırılabilir ve verilere bakılmaksızın dizenin uzunluğu ile tutarlı bir şekilde orantılıdır.

Tüm geçerli karakterlerle eşleşen ve geçersiz karakterler ararken regex yöntemleri arasında benzer bir fark var. Eşleştirme, uzun, ancak tam olarak geçerli bir dize denetlerken biraz daha iyi performans gösterir, ancak dize sonuna yakın geçersiz karakterler için daha kötüdür.

22
Brian

Bu hedefe ulaşmak için çeşitli yollar vardır, bazıları diğerlerinden daha açıktır. Örneklerimin her biri için 'Doğru', geçirilen dizenin geçerli olduğu, 'Yanlış' 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 normal bir ifadenin kullanımı var, bunu re.match () ile yapabilirsiniz. '-' nin [] sonunda olması gerektiğini unutmayın, aksi takdirde 'aralık' sınırlayıcısı olarak kullanılır. Ayrıca 'dizgenin sonu' anlamına gelen $ 'a da dikkat edin. Bu soruya not edilen diğer cevaplar özel bir karakter sınıfı kullanır: '\ w'. vaka.

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

Başka bir çözüm, düzenli ifadelerle ters bir eşleşme yapabileceğinizi belirtti, bunu şimdi buraya dahil ettim. ^ Kullanıldığından, [^ ...] karakter sınıfını tersine çevirir:

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 diziden izin verilen tüm karakterleri kaldıran, bizi a) hiçbir şey ya da b) dizeden rahatsız edici karakterleri içeren bir kümeyle bırakan bu örneğe bir göz atın:

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

Çizgiler ve alt çizgiler için olmasaydı, en kolay çözüm

my_little_string.isalnum()

(Bölüm 3.6.1 Python Kütüphanesi Referansı)

11
Ber

Regex kullanmaya alternatif olarak bunu Setler'de yapabilirsiniz:

from sets import Set

allowed_chars = Set('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-')

if Set(my_little_sting).issubset(allowed_chars):
    # your action
    print True
5
Ber
 pat = re.compile ('[^\w-]')

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

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 :)

1

Her zaman bir liste anlayışı kullanabilir ve sonuçları tümüyle kontrol edebilirsiniz, bir regex kullanmaktan daha az kaynak yoğun olurdu: all([c in string.letters + string.digits + ["_", "-"] for c in mystring])

0
William Keller

Düzenli ifade çok esnek olabilir. 

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

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

Yani - char eklemeniz gerekir.

+: Önceki karakterin bir veya daha fazla tekrarını eşleştirin. Sanırım boş girdi kabul etmiyorsun. Ancak yaparsanız, * olarak değiştirin.

^: Dizenin başlangıcını eşleştirir.

$: Dizenin sonuyla eşleşir.

Aşağıdaki iki durumdan kaçınmanız gerektiğinden bu iki özel karaktere ihtiyacınız var:

&&&PATTERN&&PATTERN

İstemediğiniz kalıp, muhtemelen istediğiniz kalıpların arasında oturabilir. 

Bu örnek için: &&& beklediğiniz durumda değil ama legal dizesi kabul edilebilir. Normal ifadeye ^ ve $ eklemezseniz, bu düzen yanlış düzenle eşleşir.

0
Alston

İşte Jerub'un "naif yaklaşımını" temel alan bir şey (saf onun sözleri, benim değil!):

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

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

ALLOWED bir dize olsaydı, bence c in ALLOWED, bir eşleşme bulana veya sonuna ulaşana kadar dizedeki her karakterin üzerinde yineleme yapmayı içerir. Joel Spolsky'den alıntı yapmak gerekirse, Ressam algoritmasını Shlemiel olan bir şey.

Ancak, bir küme içinde varlık testi, daha verimli olmalı veya en azından izin verilen karakter sayısına bağlı olmalıdır. Elbette bu yaklaşım makinemde biraz daha hızlı. Açıktır ve çoğu durumda yeterince iyi performans gösterdiğini düşünüyorum (yavaş makinemde on saniyelik kısa isikli ipleri bir saniyenin bir bölümünde doğrulayabilirim). Bunu sevdim.

ASLINDA makinemde bir regexp birkaç kat daha hızlı çalışıyor ve bu kadar basittir (tartışmasız daha basit). Yani bu muhtemelen en iyi yoldur.

0
MB.