Cette solution correspondra aux chaînes de longueur nulle. utilisez + au lieu de * pour faire correspondre les chaînes de 1 ou plusieurs caractères.
Jerub
10
@Prestaul: \wcomprend \det _, par conséquent isvalid = re.match(r'[\w-]+$', astr)ou isinvalid = re.search(r'[^\w-]', astr). Une présence possible de locale.setlocalechaînes ou de chaînes Unicode nécessite une considération supplémentaire.
jfs
1
Correction: isvalid = re.match(r'[\w-]*$', astr)- les chaînes vides sont valides.
jfs
Comment pouvez-vous également autoriser un point / point (.) Dans cette expression régulière? Modifier, voici comment: ^ [a-zA-Z0-9 -_ \ s \.] + $
fredrik
24
[Modifier] Il y a une autre solution qui n'a pas encore été mentionnée, et elle semble surpasser les autres données jusqu'à présent dans la plupart des cas.
Utilisez string.translate pour remplacer tous les caractères valides dans la chaîne et voir s'il nous en reste des non valides. C'est assez rapide car il utilise la fonction C sous-jacente pour faire le travail, avec très peu de bytecode python impliqué.
Évidemment, les performances ne font pas tout - opter pour les solutions les plus lisibles est probablement la meilleure approche lorsque ce n'est pas dans un chemin de code critique pour les performances, mais juste pour voir comment les solutions s'empilent, voici une comparaison des performances de toutes les méthodes proposées jusqu'à présent. check_trans est celui qui utilise la méthode string.translate.
Code de test:
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('','')
defcheck_set_diff(s):returnnot set(s) - allowed_set
defcheck_set_all(s):return all(x in allowed_set for x in s)
defcheck_set_subset(s):return set(s).issubset(allowed_set)
defcheck_re_match(s):return pat.match(s)
defcheck_re_inverse(s):# Search for non-matching character.returnnot pat_inv.search(s)
defcheck_trans(s):returnnot 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=''defmain():
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))
printif __name__=='__main__': main()
L'approche de traduction semble la meilleure dans la plupart des cas, de manière dramatique avec de longues chaînes valides, mais est battue par les expressions rationnelles dans test_long_invalid (probablement parce que l'expression régulière peut sortir immédiatement, mais translate doit toujours analyser toute la chaîne). Les approches d'ensemble sont généralement les pires, battant les expressions rationnelles uniquement pour la casse de la chaîne vide.
L'utilisation de all (x dans allowed_set pour x dans s) fonctionne bien si elle renverse tôt, mais peut être mauvaise si elle doit parcourir chaque caractère. isSubSet et set difference sont comparables et sont systématiquement proportionnels à la longueur de la chaîne, quelles que soient les données.
Il existe une différence similaire entre les méthodes regex correspondant à tous les caractères valides et la recherche de caractères non valides. La correspondance fonctionne un peu mieux lors de la vérification d'une chaîne longue mais entièrement valide, mais pire pour les caractères non valides près de la fin de la chaîne.
Utilisez à la string.ascii_lettersplace de string.letterssi vous n'utilisez pas le drapeau re.LOCALE pour les expressions régulières (sinon, vous pourriez obtenir des résultats faussement positifs check_trans(). string.maketrans()Ne fonctionnera pas pour les chaînes Unicode.
jfs
Pour Python 3 / Unicode / from __future__ import unicode_literals), utilisez trans_table3 = dict((ord(char), '') for char in allowed_chars)et def check_trans(s): return not s.translate(trans_table3). Mais en général, il fonctionne moins bien que les versions RE.
Hugo
14
Il existe différentes manières d'atteindre cet objectif, certaines sont plus claires que d'autres. Pour chacun de mes exemples, «True» signifie que la chaîne passée est valide, «False» signifie qu'elle contient des caractères non valides.
Tout d'abord, il y a l'approche naïve:
import string
allowed = string.letters + string.digits + '_' + '-'defcheck_naive(mystring):return all(c in allowed for c in mystring)
Ensuite, il y a l'utilisation d'une expression régulière, vous pouvez le faire avec re.match (). Notez que «-» doit être à la fin de [] sinon il sera utilisé comme délimiteur de «plage». Notez également le $ qui signifie «fin de chaîne». D'autres réponses notées dans cette question utilisent une classe de caractères spéciaux, '\ w', je préfère toujours utiliser une plage de classes de caractères explicite en utilisant [] car c'est plus facile à comprendre sans avoir à chercher un guide de référence rapide, et plus facile à spécial- Cas.
import re
CHECK_RE = re.compile('[a-zA-Z0-9_-]+$')
defcheck_re(mystring):return CHECK_RE.match(mystring)
Une autre solution a noté que vous pouvez faire une correspondance inverse avec des expressions régulières, je l'ai inclus ici maintenant. Notez que [^ ...] inverse la classe de caractères car ^ est utilisé:
Vous pouvez également faire quelque chose de délicat avec l'objet 'set'. Jetez un œil à cet exemple, qui supprime de la chaîne d'origine tous les caractères autorisés, nous laissant avec un ensemble contenant soit a) rien, soit b) les caractères incriminés de la chaîne:
Dans votre premier test de regex, "[a-zA-Z0-9 _-] + $" ne devrait pas être "[a-zA-Z0-9 _-] * $". La chaîne vide doit probablement être considérée comme correspondante.
Brian
À utiliser string.ascii_letterssi vous utilisez les expressions rationnelles «[a-zA-Z]».
jfs
12
S'il n'y avait pas les tirets et les traits de soulignement, la solution la plus simple serait
my_little_string.isalnum()
(Section 3.6.1 de la référence de la bibliothèque Python)
Comme alternative à l'utilisation de regex, vous pouvez le faire dans des ensembles:
from sets import Set
allowed_chars = Set('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-')
if Set(my_little_sting).issubset(allowed_chars):
# your actionprintTrue
import re;
re.fullmatch("^[\w-]+$", target_string) # fullmatch looks also workable for python 3.4
\w: Seulement [a-zA-Z0-9_]
Vous devez donc ajouter -char pour justifier le trait d'union.
+: Correspond à une ou plusieurs répétitions du caractère précédent. Je suppose que vous n'acceptez pas les entrées vides. Mais si vous le faites, passez à *.
^: Correspond au début de la chaîne.
$: Correspond à la fin de la chaîne.
Vous avez besoin de ces deux caractères spéciaux car vous devez éviter le cas suivant. Les caractères indésirables comme &ici peuvent apparaître entre le motif correspondant.
Eh bien, vous pouvez demander l'aide de regex, le grand ici :)
code:
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'
Vous pouvez toujours utiliser une compréhension de liste et vérifier les résultats avec tous, ce serait un peu moins gourmand en ressources que d'utiliser une regex: all([c in string.letters + string.digits + ["_", "-"] for c in mystring])
Veuillez tester votre code avant de publier. Une solution basée sur votre réponse cassée qui s'exécute est: all (c dans string.letters + string.digits + "_" pour c dans mystring)
Jerub
2
Cela va être beaucoup plus gourmand en ressources qu'une regex. Cela fait un scan linéaire pour chaque personnage (mieux vaut construire un ensemble à l'avance), et vous construisez inutilement une liste lorsqu'une compréhension de générateur serait plus légère.
Brian le
-1
Voici quelque chose basé sur "l'approche naïve" de Jerub (naïf étant ses mots, pas les miens!):
import string
ALLOWED = frozenset(string.ascii_letters + string.digits + '_' + '-')
defcheck(mystring):return all(c in ALLOWED for c in mystring)
Si ALLOWEDc'était une chaîne, je pense que c in ALLOWEDcela impliquerait une itération sur chaque caractère de la chaîne jusqu'à ce qu'elle trouve une correspondance ou atteigne la fin. Qui, pour citer Joel Spolsky, est en quelque sorte un algorithme de Shlemiel le peintre .
Mais tester l'existence dans un ensemble devrait être plus efficace, ou au moins moins dépendant du nombre de caractères autorisés. Certes, cette approche est un peu plus rapide sur ma machine. C'est clair et je pense qu'il fonctionne assez bien dans la plupart des cas (sur ma machine lente, je peux valider des dizaines de milliers de cordes courtes en une fraction de seconde). Je l'aime.
RÉELLEMENT sur ma machine, une expression rationnelle fonctionne plusieurs fois plus rapidement, et est tout aussi simple que cela (sans doute plus simple). C'est donc probablement la meilleure voie à suivre.
Tous ces personnages doivent être dans une classe, sinon vous obtiendrez de faux négatifs. Vous avez également oublié d'inclure les marqueurs de début et de fin de chaîne ... comme ceci, il correspondra toujours tant qu'un caractère valide est présent.
Thomas
1
Cela correspondra en fait même s'il n'y a pas de caractères valides. Correspondance de longueur nulle. De plus, ce n'est pas en python.
Réponses:
Une expression régulière fera l'affaire avec très peu de code:
import re ... if re.match("^[A-Za-z0-9_-]*$", my_little_string): # do something here
la source
\w
comprend\d
et_
, par conséquentisvalid = re.match(r'[\w-]+$', astr)
ouisinvalid = re.search(r'[^\w-]', astr)
. Une présence possible delocale.setlocale
chaînes ou de chaînes Unicode nécessite une considération supplémentaire.isvalid = re.match(r'[\w-]*$', astr)
- les chaînes vides sont valides.[Modifier] Il y a une autre solution qui n'a pas encore été mentionnée, et elle semble surpasser les autres données jusqu'à présent dans la plupart des cas.
Utilisez string.translate pour remplacer tous les caractères valides dans la chaîne et voir s'il nous en reste des non valides. C'est assez rapide car il utilise la fonction C sous-jacente pour faire le travail, avec très peu de bytecode python impliqué.
Évidemment, les performances ne font pas tout - opter pour les solutions les plus lisibles est probablement la meilleure approche lorsque ce n'est pas dans un chemin de code critique pour les performances, mais juste pour voir comment les solutions s'empilent, voici une comparaison des performances de toutes les méthodes proposées jusqu'à présent. check_trans est celui qui utilise la méthode string.translate.
Code de test:
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()
Les résultats sur mon système sont:
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'approche de traduction semble la meilleure dans la plupart des cas, de manière dramatique avec de longues chaînes valides, mais est battue par les expressions rationnelles dans test_long_invalid (probablement parce que l'expression régulière peut sortir immédiatement, mais translate doit toujours analyser toute la chaîne). Les approches d'ensemble sont généralement les pires, battant les expressions rationnelles uniquement pour la casse de la chaîne vide.
L'utilisation de all (x dans allowed_set pour x dans s) fonctionne bien si elle renverse tôt, mais peut être mauvaise si elle doit parcourir chaque caractère. isSubSet et set difference sont comparables et sont systématiquement proportionnels à la longueur de la chaîne, quelles que soient les données.
Il existe une différence similaire entre les méthodes regex correspondant à tous les caractères valides et la recherche de caractères non valides. La correspondance fonctionne un peu mieux lors de la vérification d'une chaîne longue mais entièrement valide, mais pire pour les caractères non valides près de la fin de la chaîne.
la source
string.ascii_letters
place destring.letters
si vous n'utilisez pas le drapeau re.LOCALE pour les expressions régulières (sinon, vous pourriez obtenir des résultats faussement positifscheck_trans()
.string.maketrans()
Ne fonctionnera pas pour les chaînes Unicode.from __future__ import unicode_literals
), utiliseztrans_table3 = dict((ord(char), '') for char in allowed_chars)
et defcheck_trans(s): return not s.translate(trans_table3)
. Mais en général, il fonctionne moins bien que les versions RE.Il existe différentes manières d'atteindre cet objectif, certaines sont plus claires que d'autres. Pour chacun de mes exemples, «True» signifie que la chaîne passée est valide, «False» signifie qu'elle contient des caractères non valides.
Tout d'abord, il y a l'approche naïve:
import string allowed = string.letters + string.digits + '_' + '-' def check_naive(mystring): return all(c in allowed for c in mystring)
Ensuite, il y a l'utilisation d'une expression régulière, vous pouvez le faire avec re.match (). Notez que «-» doit être à la fin de [] sinon il sera utilisé comme délimiteur de «plage». Notez également le $ qui signifie «fin de chaîne». D'autres réponses notées dans cette question utilisent une classe de caractères spéciaux, '\ w', je préfère toujours utiliser une plage de classes de caractères explicite en utilisant [] car c'est plus facile à comprendre sans avoir à chercher un guide de référence rapide, et plus facile à spécial- Cas.
import re CHECK_RE = re.compile('[a-zA-Z0-9_-]+$') def check_re(mystring): return CHECK_RE.match(mystring)
Une autre solution a noté que vous pouvez faire une correspondance inverse avec des expressions régulières, je l'ai inclus ici maintenant. Notez que [^ ...] inverse la classe de caractères car ^ est utilisé:
CHECK_INV_RE = re.compile('[^a-zA-Z0-9_-]') def check_inv_re(mystring): return not CHECK_INV_RE.search(mystring)
Vous pouvez également faire quelque chose de délicat avec l'objet 'set'. Jetez un œil à cet exemple, qui supprime de la chaîne d'origine tous les caractères autorisés, nous laissant avec un ensemble contenant soit a) rien, soit b) les caractères incriminés de la chaîne:
def check_set(mystring): return not set(mystring) - set(allowed)
la source
string.ascii_letters
si vous utilisez les expressions rationnelles «[a-zA-Z]».S'il n'y avait pas les tirets et les traits de soulignement, la solution la plus simple serait
(Section 3.6.1 de la référence de la bibliothèque Python)
la source
Comme alternative à l'utilisation de regex, vous pouvez le faire dans des ensembles:
from sets import Set allowed_chars = Set('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-') if Set(my_little_sting).issubset(allowed_chars): # your action print True
la source
pat = re.compile ('[^\w-]') def onlyallowed(s): return not pat.search (s)
la source
L'expression régulière peut être très flexible.
import re; re.fullmatch("^[\w-]+$", target_string) # fullmatch looks also workable for python 3.4
\w
: Seulement[a-zA-Z0-9_]
Vous devez donc ajouter
-
char pour justifier le trait d'union.+
: Correspond à une ou plusieurs répétitions du caractère précédent. Je suppose que vous n'acceptez pas les entrées vides. Mais si vous le faites, passez à*
.^
: Correspond au début de la chaîne.$
: Correspond à la fin de la chaîne.Vous avez besoin de ces deux caractères spéciaux car vous devez éviter le cas suivant. Les caractères indésirables comme
&
ici peuvent apparaître entre le motif correspondant.&&&PATTERN&&PATTERN
la source
Eh bien, vous pouvez demander l'aide de regex, le grand ici :)
code:
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'
Production:
J'espère que cela t'aides :)
la source
Vous pouvez toujours utiliser une compréhension de liste et vérifier les résultats avec tous, ce serait un peu moins gourmand en ressources que d'utiliser une regex:
all([c in string.letters + string.digits + ["_", "-"] for c in mystring])
la source
Voici quelque chose basé sur "l'approche naïve" de Jerub (naïf étant ses mots, pas les miens!):
import string ALLOWED = frozenset(string.ascii_letters + string.digits + '_' + '-') def check(mystring): return all(c in ALLOWED for c in mystring)
Si
ALLOWED
c'était une chaîne, je pense quec in ALLOWED
cela impliquerait une itération sur chaque caractère de la chaîne jusqu'à ce qu'elle trouve une correspondance ou atteigne la fin. Qui, pour citer Joel Spolsky, est en quelque sorte un algorithme de Shlemiel le peintre .Mais tester l'existence dans un ensemble devrait être plus efficace, ou au moins moins dépendant du nombre de caractères autorisés. Certes, cette approche est un peu plus rapide sur ma machine. C'est clair et je pense qu'il fonctionne assez bien dans la plupart des cas (sur ma machine lente, je peux valider des dizaines de milliers de cordes courtes en une fraction de seconde). Je l'aime.
RÉELLEMENT sur ma machine, une expression rationnelle fonctionne plusieurs fois plus rapidement, et est tout aussi simple que cela (sans doute plus simple). C'est donc probablement la meilleure voie à suivre.
la source
utilisez une regex et voyez si elle correspond!
([a-z][A-Z][0-9]\_\-)*
la source