python - online - ¿Cómo verifico que una cadena solo contiene letras, números, guiones bajos y guiones?
python split regex (10)
Sé cómo hacer esto si repito todos los caracteres de la cadena, pero estoy buscando un método más elegante.
¡use una expresión regular y vea si coincide!
([a-z][A-Z][0-9]/_/-)*
Aquí hay algo basado en el "enfoque ingenuo" de Jerub (¡siendo ingenuo sus palabras, no las mías!):
import string
ALLOWED = frozenset(string.ascii_letters + string.digits + ''_'' + ''-'')
def check(mystring):
return all(c in ALLOWED for c in mystring)
Si ALLOWED
era una cadena, entonces creo que c in ALLOWED
implicaría iterar sobre cada carácter en la cadena hasta que encuentre una coincidencia o llegue al final. Lo cual, para citar a Joel Spolsky, es algo así como un algoritmo de Shlemiel the Painter .
Pero probar la existencia en un conjunto debería ser más eficiente, o al menos menos dependiente de la cantidad de caracteres permitidos. Ciertamente, este enfoque es un poco más rápido en mi máquina. Está claro y creo que funciona bastante bien para la mayoría de los casos (en mi máquina lenta puedo validar decenas de miles de cadenas cortas en una fracción de segundo). Me gusta.
REALMENTE en mi máquina una expresión regular funciona varias veces más rápido, y es tan simple como esto (podría decirse que más simple). Entonces, ese es probablemente el mejor camino a seguir.
Bueno, puedes pedir la ayuda de Regex, el gran aquí :)
código:
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''
Salida:
yes
Espero que esto ayude :)
Como alternativa al uso de expresiones regulares, puedes hacerlo en Conjuntos:
from sets import Set
allowed_chars = Set(''0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-'')
if Set(my_little_sting).issubset(allowed_chars):
# your action
print True
Hay una variedad de formas de lograr este objetivo, algunas son más claras que otras. Para cada uno de mis ejemplos, ''Verdadero'' significa que la secuencia que se pasa es válida, ''Falsa'' significa que contiene caracteres no válidos.
En primer lugar, está el enfoque ingenuo:
import string
allowed = string.letters + string.digits + ''_'' + ''-''
def check_naive(mystring):
return all(c in allowed for c in mystring)
Luego está el uso de una expresión regular, puede hacer esto con re.match (). Tenga en cuenta que ''-'' debe estar al final de [] de lo contrario se usará como un delimitador ''rango''. También tenga en cuenta el $ que significa ''final de la cadena''. Otras respuestas indicadas en esta pregunta usan una clase de caracteres especial, ''/ w'', siempre prefiero usar un rango de clase de caracteres explícito usando [] porque es más fácil de entender sin tener que buscar una guía de referencia rápida, y más fácil de especializar. caso.
import re
CHECK_RE = re.compile(''[a-zA-Z0-9_-]+$'')
def check_re(mystring):
return CHECK_RE.match(mystring)
Otra solución señaló que puede hacer una combinación inversa con expresiones regulares, lo he incluido aquí ahora. Tenga en cuenta que [^ ...] invierte la clase de caracteres porque se usa ^
CHECK_INV_RE = re.compile(''[^a-zA-Z0-9_-]'')
def check_inv_re(mystring):
return not CHECK_INV_RE.search(mystring)
También puede hacer algo complicado con el objeto ''establecer''. Eche un vistazo a este ejemplo, que elimina de la cadena original todos los caracteres que están permitidos, dejándonos con un conjunto que contiene a) nada, o b) los caracteres ofensivos de la cadena:
def check_set(mystring):
return not set(mystring) - set(allowed)
Si no fuera por los guiones y los guiones bajos, la solución más fácil sería
my_little_string.isalnum()
(Sección 3.6.1 de la Referencia de la Biblioteca de Python)
Siempre puedes usar una lista de comprensión y verificar los resultados con todo, sería un poco menos intensivo en recursos que usar una expresión regular: all([c in string.letters + string.digits + ["_", "-"] for c in mystring])
Una expresión regular hará el truco con muy poco código:
import re
...
if re.match("^[A-Za-z0-9_-]*$", my_little_string):
# do something here
[Editar] Hay otra solución aún no mencionada, y parece superar a los otros dados hasta ahora en la mayoría de los casos.
Utilice string.translate para reemplazar todos los caracteres válidos en la cadena, y vea si nos quedan algunos inválidos. Esto es bastante rápido ya que usa la función C subyacente para hacer el trabajo, con muy poco bytecode de python involucrado.
Obviamente, el rendimiento no lo es todo; ir por las soluciones más legibles es probablemente el mejor enfoque cuando no está en un código de ruta crítico, pero para ver cómo se compaginan las soluciones, aquí hay una comparación de rendimiento de todos los métodos propuestos hasta ahora. check_trans es el que usa el método string.translate.
Código de prueba:
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()
Los resultados en mi sistema son:
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
El enfoque de traducción parece ser el mejor en la mayoría de los casos, de forma espectacular con cadenas largas válidas, pero es superado por expresiones regulares en test_long_invalid (Presumiblemente porque la expresión regular puede rescatar inmediatamente, pero la traducción siempre tiene que analizar toda la cadena). Los enfoques establecidos suelen ser peores, superando las expresiones regulares solo para el caso de cadena vacía.
Usar todo (x en permitido_juego para x en s) funciona bien si se rescata temprano, pero puede ser malo si tiene que recorrer cada carácter. isSubSet y set difference son comparables, y son consistentemente proporcionales a la longitud de la cadena, independientemente de los datos.
Hay una diferencia similar entre los métodos regex que coinciden con todos los caracteres válidos y la búsqueda de caracteres no válidos. La coincidencia funciona un poco mejor cuando se busca una cadena larga, pero totalmente válida, pero peor para los caracteres no válidos cerca del final de la cadena.
pat = re.compile (''[^/w-]'')
def onlyallowed(s):
return not pat.search (s)