So come farlo se itero tutti i caratteri nella stringa, ma cerco un metodo più elegante.
So come farlo se itero tutti i caratteri nella stringa, ma cerco un metodo più elegante.
Risposte:
Un'espressione regolare farà il trucco con pochissimo codice:
import re
...
if re.match("^[A-Za-z0-9_-]*$", my_little_string):
# do something here
\winclude \de _, quindi isvalid = re.match(r'[\w-]+$', astr)o isinvalid = re.search(r'[^\w-]', astr). Una possibile presenza di locale.setlocaleo stringhe Unicode richiede un'ulteriore considerazione.
isvalid = re.match(r'[\w-]*$', astr)- le stringhe vuote sono valide.
[Modifica] C'è un'altra soluzione non ancora menzionata, e nella maggior parte dei casi sembra superare le altre date finora.
Usa string.translate per sostituire tutti i caratteri validi nella stringa e controlla se ne sono rimasti di quelli non validi. Questo è abbastanza veloce in quanto utilizza la funzione C sottostante per fare il lavoro, con pochissimo bytecode Python coinvolto.
Ovviamente le prestazioni non sono tutto: scegliere le soluzioni più leggibili è probabilmente l'approccio migliore quando non ci si trova in un codepath critico per le prestazioni, ma solo per vedere come si accumulano le soluzioni, ecco un confronto delle prestazioni di tutti i metodi proposti finora. check_trans è quello che utilizza il metodo string.translate.
Codice di prova:
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()
I risultati sul mio sistema sono:
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
L'approccio di traduzione sembra il migliore nella maggior parte dei casi, drammaticamente con stringhe valide lunghe, ma è battuto dalle espressioni regolari in test_long_invalid (presumibilmente perché la regex può salvarsi immediatamente, ma tradurre deve sempre scansionare l'intera stringa). Gli approcci impostati sono generalmente i peggiori, battendo le espressioni regolari solo per il caso di stringa vuota.
L'uso di all (x in allowed_set per x in s) funziona bene se esce presto, ma può essere negativo se deve scorrere ogni carattere. isSubSet e set difference sono confrontabili e sono costantemente proporzionali alla lunghezza della stringa indipendentemente dai dati.
C'è una differenza simile tra i metodi regex che corrispondono a tutti i caratteri validi e alla ricerca di caratteri non validi. La corrispondenza funziona un po 'meglio quando si controlla una stringa lunga ma completamente valida, ma peggio per i caratteri non validi vicino alla fine della stringa.
string.ascii_lettersinvece di string.lettersse non usi il flag re.LOCALE per le espressioni regolari (altrimenti potresti ottenere risultati falsi positivi in check_trans(). string.maketrans()Non funzionerà per le stringhe unicode.
from __future__ import unicode_literals), usa trans_table3 = dict((ord(char), '') for char in allowed_chars)e def check_trans(s): return not s.translate(trans_table3). Ma in generale, ha prestazioni peggiori rispetto alle versioni RE.
Esistono diversi modi per raggiungere questo obiettivo, alcuni sono più chiari di altri. Per ciascuno dei miei esempi, "True" significa che la stringa passata è valida, "False" significa che contiene caratteri non validi.
Prima di tutto, c'è l'approccio ingenuo:
import string
allowed = string.letters + string.digits + '_' + '-'
def check_naive(mystring):
return all(c in allowed for c in mystring)
Poi c'è l'uso di un'espressione regolare, puoi farlo con re.match (). Nota che "-" deve essere alla fine di [] altrimenti verrà utilizzato come delimitatore di "intervallo". Nota anche il $ che significa "fine della stringa". Altre risposte annotate in questa domanda utilizzano una classe di caratteri speciale, '\ w', preferisco sempre usare un intervallo di classi di caratteri esplicito usando [] perché è più facile da capire senza dover cercare una guida di riferimento rapido e più facile da speciale- Astuccio.
import re
CHECK_RE = re.compile('[a-zA-Z0-9_-]+$')
def check_re(mystring):
return CHECK_RE.match(mystring)
Un'altra soluzione ha notato che puoi fare una corrispondenza inversa con le espressioni regolari, l'ho inclusa qui ora. Nota che [^ ...] inverte la classe del carattere perché viene utilizzato ^:
CHECK_INV_RE = re.compile('[^a-zA-Z0-9_-]')
def check_inv_re(mystring):
return not CHECK_INV_RE.search(mystring)
Puoi anche fare qualcosa di complicato con l'oggetto "set". Dai un'occhiata a questo esempio, che rimuove dalla stringa originale tutti i caratteri consentiti, lasciandoci con un set contenente o a) niente, oppure b) i caratteri offensivi dalla stringa:
def check_set(mystring):
return not set(mystring) - set(allowed)
string.ascii_lettersse si utilizzano espressioni regolari "[a-zA-Z]".
Se non fosse per i trattini e le sottolineature, la soluzione più semplice sarebbe
my_little_string.isalnum()
(Sezione 3.6.1 della Python Library Reference)
L'espressione regolare può essere molto flessibile.
import re;
re.fullmatch("^[\w-]+$", target_string) # fullmatch looks also workable for python 3.4
\w: Solo [a-zA-Z0-9_]
Quindi è necessario aggiungere -char per giustificare il trattino char.
+: Corrisponde a una o più ripetizioni del carattere precedente. Immagino che tu non accetti input in bianco. Ma se lo fai, passa a *.
^: Corrisponde all'inizio della stringa.
$: Corrisponde alla fine della stringa.
Questi due caratteri speciali sono necessari poiché è necessario evitare il seguente caso. I caratteri indesiderati come &qui potrebbero apparire tra il modello corrispondente.
&&&PATTERN&&PATTERN
Bene, puoi chiedere l'aiuto di regex, il grande qui :)
codice:
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'
Produzione:
yes
Spero che sia di aiuto :)
Puoi sempre usare una comprensione dell'elenco e controllare i risultati con tutti, sarebbe un po 'meno dispendioso in termini di risorse rispetto all'utilizzo di una regex: all([c in string.letters + string.digits + ["_", "-"] for c in mystring])
Ecco qualcosa basato sull '"approccio ingenuo" di Jerub (ingenue sono le sue parole, non le mie!):
import string
ALLOWED = frozenset(string.ascii_letters + string.digits + '_' + '-')
def check(mystring):
return all(c in ALLOWED for c in mystring)
Se ALLOWEDfosse una stringa, penso c in ALLOWEDche comporterebbe l'iterazione su ogni carattere nella stringa fino a quando non trova una corrispondenza o raggiunge la fine. Il che, per citare Joel Spolsky, è una specie di algoritmo di Shlemiel the Painter .
Ma testare l'esistenza in un insieme dovrebbe essere più efficiente, o almeno meno dipendente dal numero di caratteri consentiti. Sicuramente questo approccio è un po 'più veloce sulla mia macchina. È chiaro e penso che funzioni abbastanza bene per la maggior parte dei casi (sulla mia macchina lenta posso convalidare decine di migliaia di stringhe corte in una frazione di secondo). Mi piace.
ATTUALMENTE sulla mia macchina una regexp funziona molte volte più velocemente ed è altrettanto semplice (probabilmente più semplice). Quindi questa probabilmente è la migliore via da seguire.
usa una regex e vedi se corrisponde!
([a-z][A-Z][0-9]\_\-)*