Si no sabe lo que expresión regular (también conocido como "regex" o "regexp") es, es un un lenguaje increíblemente poderoso para ejecutar una búsqueda y/o sustitución a través de cualquier tipo de texto. Regex ha tenido una larga y gloriosa historia (sus orígenes se remontan a los años 50) e incluso ahora, se utiliza a diario... puede que no lo sepas. Regex está integrado en la mayoría de los principales lenguajes de programación y sistemas informáticos y se utiliza en una gran variedad de aplicaciones.
Regex ya no es sólo para programadores: hoy en día aparece en todo tipo de lugares. Algunos de los lugares en los que puede que lo hayas visto hasta ahora es para hacer coincidir URLs en Google Analytics, buscar y reemplazar en tu editor de texto favorito (algunos editores populares que soportan regex incluyen Sublime, Atom, Nube9, Bloc de notas, Google Docs y Microsoft Word ... aunque Word tiene una sintaxis regex que es muy no estándar) e incluso nombres de archivo coincidentes en nuestro impresionante plugin de afiliación de WordPress, MemberPress.
Aunque la regex aparece ahora en lugares sorprendentemente nuevos, su naturaleza aparentemente críptica probablemente hará que siempre sea más utilizada por programadores y "usuarios avanzados".
Así que aquí está el punto de este artículo en el que tienes que decidir si vas a seguir viviendo sin conocer el incalculable poder de regex, o si te tragarás la píldora roja y blandirás la espada de un usuario avanzado. En este post, te daré los conocimientos básicos que necesitarás para empezar a poner esta herramienta extremadamente útil a trabajar para ti. Así que, ¡empecemos!
Algunos ejemplos
¿Qué aspecto tienen las expresiones regulares? Si vieras una sin tener ni idea de lo que es, te parecería un completo galimatías.
A continuación se muestra una sentencia regexp de ejemplo para hacer coincidir un número de teléfono:
\(¿? d {3})?[- ]\d{3}-\d{4}
Aquí hay uno que coincide con una URL:
https?:\/\/[\w-]+(\.[\w-]{2,})*(:\d{1,5})?
Y uno que pueda coincidir con una dirección de correo electrónico:
[\w\d._%+-]+@[\w\d.-]+\.[\w]{2,4}
Por último, he aquí una sencilla búsqueda y sustitución que encontrará todos los nombres de dominio con el subdominio "test" y los cambiará por un subdominio "www":
Busca: test\.([^\.]+)\.com
Sustitúyelo: www.$1.com
¿Intimidado ya? No lo hagas. Repasaré cómo se interpretan las sentencias regex, desglosando lo que hacemos en cada una de ellas y explicando cómo crear las tuyas propias.
Cómo leer expresiones regulares
Las expresiones regulares son como las sentencias de búsqueda normales, pero con esteroides. Por ejemplo, si quisieras encontrar el nombre "Harry" en una página web, harías clic en control-f y escribirías "Harry" para encontrarlo. Con regex puedes hacer más que simplemente buscar "Harry". Puedes buscar "Harry" o "Bob" con (Harry|Bob)
o puede buscar cualquier palabra que empiece por "Ha" con Ha\S*
.
Para tu ordenador, el texto se representa como una "cadena" de caracteres. De hecho, si alguna vez has escuchado a programadores hablando de código, probablemente les hayas oído utilizar el término "cadena" para referirse a alguna variable.
Las expresiones regulares se leen de izquierda a derecha (como el inglés y muchos otros idiomas) y explican al ordenador con qué debe coincidir cuando escanea una cadena de izquierda a derecha. A medida que el ordenador mira cada carácter, sigue este flujo:
Si observa este diagrama de flujo, por complicado que parezca, verá algo muy familiar. Es prácticamente idéntico a como esperarías cualquier en un ordenador y cómo se realizan las búsquedas con las que ya estás familiarizado. Regex sólo añade algunas características muy potentes en la parte superior de la búsqueda normal.
Conceptos básicos
Ahora, antes de empezar a diseccionar nuestro ejemplo regex declaraciones de arriba, vamos a pasar por algunos de los caracteres de comando muy básico de regex:
Repetidores: *, + y {...}
Los caracteres asterisco (*) y más (+) son repetidores. Un repetidor sólo se utiliza después de otro carácter o sentencia adjunta y le dice al ordenador que haga coincidir el carácter precedente X número de veces. El asterisco coincidirá con el carácter precedente de 0 a infinitas veces; el signo más, de 1 a infinitas veces; {2}
coincidirá 2 veces exactamente; {4,6}
lo hará coincidir entre 4 y 6 veces exactamente; y {7,}
lo igualará entre 7 e infinitas veces.
Comodín: .
Probablemente esté acostumbrado a utilizar el símbolo de la estrella como comodín, pero en regex se trata de un punto (.). El punto simplemente coincidirá con cualquier carácter.
Opcional: ?
Al igual que el repetidor, el carácter opcional, un signo de interrogación (?), sólo se utiliza después de otro carácter o sentencia adjunta. Cuando está colocado, indica al ordenador que el carácter precedente puede o no estar presente en los resultados de la búsqueda.
Principio y final de la cadena: ^ y $
Cuando el signo de intercalación (^) se utiliza al principio de la expresión regular, indica que debe coincidir con el principio de la cadena. Y el signo del dólar ($), cuando se utiliza al final de la expresión regular, coincidirá con el final de la cadena.
Personajes posibles: [...]
Los caracteres entre paréntesis indican los caracteres que pueden coincidir en esa posición. Por ejemplo, si desea buscar una "n", "m", "l" o "_", puede añadir lo siguiente a la expresión regular [nml_]
. Si sólo quisieras hacer coincidir cualquier letra o número podrías añadir esto [A-Za-z0-9]
.
Si desea que coincida con cualquier carácter excepto los caracteres posibles, basta con poner un signo de intercalación (^) después del paréntesis de apertura. Por ejemplo, si quiere que coincida cualquier carácter excepto 'x','y','z' o '_', puede hacerlo con la sentencia: [^xyz_]
. Como puede ver, esto le permite controlar con gran precisión las coincidencias carácter por carácter.
Declaraciones adjuntas: (...)
A veces querrá que las secciones de su regex se comporten como un bloque o guardarlas para más tarde. Para ello, basta con encerrar la expresión entre paréntesis.
Escapar: \
Ahora, ¿qué pasa si quieres buscar los caracteres '+', '.', etc.? Bueno, si le pones una barra invertida delante, le dirás a tu ordenador que estás intentando buscar un carácter de búsqueda real, y que no lo interprete como un carácter de comando.
Caracteres taquigráficos: /s, /S, /d, /D, /w, /W y /b
Estos caracteres son muy útiles y te ayudarán a emparejar determinados conjuntos de caracteres. He aquí cómo se desglosan:
/s
coincide con cualquier carácter de espacio en blanco, como el espacio y el tabulador/S
coincide con cualquier carácter que no sea un espacio en blanco/d
coincide con cualquier carácter numérico/D
coincide con cualquier carácter que no sea un dígito/w
coincide con cualquier carácter de palabra (básicamente alfanumérico)/W
coincide con cualquier carácter que no sea una palabra/b
coincide con cualquier límite de palabra (esto incluiría espacios, guiones, comas, puntos y comas, etc.)
Por supuesto, hay muchas otras cosas más potentes que puedes hacer con regex, pero con estas nociones básicas tendrás las herramientas que necesitas para 90% tus necesidades de búsqueda avanzada.
Desglose de nuestro ejemplo de número de teléfono
Bien, ahora que ya tienes las herramientas, veamos nuestros ejemplos para ver cómo funcionan, empezando por nuestra coincidencia para el número de teléfono:
\(¿? d {3})?[- ]\d{3}-\d{4}
Así que lo desglosaremos por declaraciones:
\(?
Esta es la primera sentencia que buscará el ordenador. Comienza con un carácter de escape (\) seguido de un paréntesis abierto (() seguido de un carácter opcional (?). Esto le indica al regex que busque un paréntesis abierto real para señalar el inicio de un número de teléfono, pero que puede no estar presente en todas las instancias de un número de teléfono.
\d{3}
El segundo enunciado comienza con el carácter abreviado para dígito (\d) y luego tiene un repetidor ({3}) que requerirá que haya 3 dígitos. No termina con un signo de interrogación porque estos tres dígitos no son opcionales: deben estar presentes para que el resultado de la búsqueda se considere un número de teléfono.
\)?
Ahora el ordenador comprobará si hay un paréntesis de cierre opcional, pero si no está presente puede seguir coincidiendo.
[- ]
Después de eso, tenemos una declaración de caracteres posibles que buscará un espacio o un guión para coincidir.
\d{3}-\d{4}
Por último, tenemos algunas sentencias que esperan 3 dígitos seguidos de un guión y a continuación 4 dígitos.
Puedes verlo en acción y jugar con él aquí: http://regex101.com/r/nW9iC5/3
Algunas cosas que puede notar sobre esta expresión regular es que sólo coincidirá con dos formatos aceptables para los números de teléfono:
(888) 888-8888
888-888-8888
No coincidirá con una multitud de formatos de números de teléfono inaceptables como:
8888888888
88-8888-8888
Pero se también coinciden con algunos formatos que podrían no ser apropiados:
(888-888-8888
888) 888-8888
(888)-888-8888
¿Qué crees que podrías hacer para asegurarte de que esta regex sólo coincide con los formatos adecuados?
Desglose de nuestra URL Regex
En primer lugar, me gustaría señalar que la siguiente expresión regular no es de ninguna manera una expresión regular exhaustiva de coincidencia de URL. Sólo coincidirá con una dirección web sin argumentos y tiene varios otros problemas, pero serviría para coincidir con muchas URLs de manera efectiva.
https?:\/\/[\w-]+(\.[\w-]+)*(:\d{1,5})?
Ahora el desglose:
https?:\/\/
Esta primera declaración coincide con el protocolo de la URL. Como la "s" tiene un signo de interrogación detrás, es opcional. También hay caracteres de escape delante de las barras inclinadas, que es lo que suele ocurrir en las expresiones regulares para que coincidan con barras inclinadas. Así que, en realidad, las dos únicas maneras en que una URL podría coincidir con esta declaración sería comenzar con http://
o https://
.
[\w-]+
Aquí tenemos una declaración de caracteres posibles seguida de un signo más. Esto significa que los caracteres siguientes deben ser 1 o más caracteres de palabra o guiones.
(\.[\w-]+)*
Aquí tenemos la sentencia que coincidirá con un punto seguido de caracteres de palabra o guiones. Toda esta sentencia está encerrada entre paréntesis y seguida de un signo más que indica al ordenador que podemos repetir toda esta secuencia cero o más veces. Así, esta sección podría coincidir con .memberpress
o .memberpress.com
o .memberpress.es
etc.
(:\d{1,5})?
Por último, esta declaración proporcionará un número de puerto opcional que se añadirá al final de la URL, que incluye dos puntos seguidos de 1 a 5 dígitos, luego entre paréntesis y seguido de un signo de interrogación para que toda la declaración sea opcional.
Aquí es donde puedes jugar con esto: http://regex101.com/r/vL5uZ2/2
Esta expresión regular coincidirá:
- https://memberpress.com
- https://memberpress.com
- http://localhost:3000
PERO no coincidirá con ningún parámetro que siga a una URL como ésta:
- http://localhost:3000?test=1&page=5
¿Qué podría añadir a esta expresión regular para que coincidiera con algunos parámetros al final de una URL?
Si está interesado en el regex de coincidencia de URL más completo (que coincida con parámetros, caracteres unicode, TLDs grandes, etc.) hay muchos que puede consultar, pero aparentemente el más completo y preciso es el creado por Diego Perini, que que puedes consultar en su gist de Github.
Desglose de nuestra Regex de correo electrónico
Otro uso habitual de la regex es la comparación de direcciones de correo electrónico. Nuestra regex de coincidencia de correo electrónico coincidirá con un número considerable de direcciones de correo electrónico:
[\w\d\._%+-]+@[\w\d.-]+\.[\w]{2,4}
He aquí el desglose:
[\w\d\._%+-]+
Esta sentencia coincidirá con uno o más caracteres de palabra, dígitos, puntos, guiones bajos, símbolos de porcentaje, símbolos más o guiones.
@
Toda dirección de correo electrónico necesita un símbolo "@" y eso es lo que coincide, por supuesto.
[\w\d.-]+
Después del símbolo "@" tenemos que empezar por el nombre de dominio. Esta sentencia coincidirá con uno o más caracteres de palabras, dígitos, puntos o guiones.
\.[\w]{2,4}
Ahora tenemos que hacer coincidir el dominio de nivel superior, que es un punto seguido de 2 a 4 caracteres de palabra.
También puede jugar con esta expresión regular aquí: http://regex101.com/r/yQ2wP9/1
Aunque esta expresión regular no es 100% completa, debería poder coincidir con la mayoría de las direcciones.
Buscar y reemplazar Regex
Por lo tanto, en muchos casos no sólo le interesará hacer coincidir patrones en su texto con regex, sino también utilizar la potencia de buscar y reemplazar con regex.
Utilicemos el ejemplo anterior de búsqueda y sustitución:
Busca: test\.([^\.]+)\.com
Sustitúyelo: www.$1.com
He aquí el desglose, empezando por el patrón de búsqueda:
prueba.
Esta primera parte coincidirá con cualquier parte de la cadena que empiece por "test".
([^\.]+)
Esta sentencia coincidirá con uno o más caracteres que sean cualquier cosa excepto un punto. Toda la sentencia se encierra entre paréntesis por una razón distinta de las que hemos visto hasta ahora: esta vez es para guardar lo que coincida con esta sentencia para utilizarlo en el reemplazo.
|.com
Por último, debe coincidir con un ".com" al final.
Ahora, en el reemplazo podemos usar "$1" para incluir lo que hayamos guardado entre los paréntesis en el patrón de búsqueda. Si tuvieras una segunda sentencia como esta en el patrón de búsqueda podrías usarla en el reemplazo usando "$2".
Puedes jugar con esta regex de búsqueda y sustitución aquí: http://regex101.com/r/bW5vX8/1
Sin embargo, este patrón de búsqueda tiene algunas limitaciones reales. Sólo puede coincidir con URL que empiecen por "test." y terminen en ".com". ¿Cómo cree que podría modificarlo para que coincidiera con las URL que terminan en ".com", ".net" u ".org"?
Más información sobre Regex
Este manual te ha proporcionado un curso intensivo sobre expresiones regulares, pero hay mucho más que aprender. Puedes obtener más información sobre regex en Wikipedia o expresiones regulares.info.
Hola Blair, lo único que me gusta de tus posts es la singularidad de los temas. Hoy en día lees las mismas cosas una y otra vez... todos esos artículos reescritos no se sabe cuántas veces. Yo leo cada uno de tus posts porque usas dos cosas que no muchos marketers usan: experiencia y profesionalidad. Gracias por los post tan interesantes y por favor, sigue así. Todos necesitamos gente como tú 🙂 .
Gracias Ivo ...
Tengo un problema con los formularios que encierran al usuario en un formato específico para el número de teléfono. He observado que en muchos formularios de contacto de empresas que ofrecen servicios técnicos a usuarios internacionales se exige un número de teléfono. Sin embargo, fuera de EE.UU., los números de teléfono pueden tener formatos diferentes.
Por ejemplo:
+45 8765-4321
+43 1-123-456-7890
+46 987-654-321
En muchos casos, se obliga al usuario a proporcionar un número falso para completar el formulario. Tomemos el primer ejemplo; casualmente se ajusta al típico formato forzado de muchos formularios en línea. Sin embargo, la persona que lea el formulario creerá que el número es de Oregón y no de Dinamarca. Se podría pensar que las empresas que se promocionan internacionalmente son conscientes de ello. ¿Qué haría usted al respecto?
Laurence, gracias por el comentario... planteas una buena cuestión. En este artículo, mi expresión regular es bastante rígido y atado a los números de teléfono de EE.UU.. Pero lo bueno de Regex es que se puede hacer tan floja o rígida como quieras. Por ejemplo, si yo sólo quería para que coincida con números enteros, espacios y guiones posiblemente precedida por un más que podía hacer esto:
Esta expresión regular coincidiría con cada uno de los ejemplos anteriores, pero es realmente liberal. También coincidiría con patrones sin sentido como:
+– — — —- —
+4-9-1-2-0-1-3-0-2
600-001-001-005-3838-38-28383
Posiblemente, un enfoque mejor sería no permitir ningún guión, espacio o paréntesis en el número si desea admitir formatos internacionales. Un ejemplo sería:
Eso coincidiría con todos tus números anteriores sin guiones:
+4587654321
+4311234567890
+46987654321
Esta solución sería genial, pero también es extremadamente liberal... no tendrías forma de validar el código de país ni nada por el estilo.
Otra posible solución, si sabe a qué países se dirige, sería incluir en la expresión regular todos los formatos posibles que le interese admitir:
Este patrón coincidiría con cada uno de los tres ejemplos anteriores, pero no coincidiría con ningún número de teléfono de EE.UU., etc. Proporciona una validación mejor que las expresiones regulares anteriores porque es más rígida... pero tampoco validará ningún número que no forme parte de los códigos de país 45, 43 y 46.
Tal vez si googleas un poco puedas encontrar una regex maestra para validar números de teléfono de todo el mundo. Yo todavía no he encontrado ninguna... así que si encuentras alguna, házmelo saber. Alternativamente, usted podría escribir su propio regex integral para esto (estoy seguro de que sería muy popular) ... sólo tendría que utilizar una técnica como la que hice en el último regex aquí ... pero que abarca todos los formatos numéricos de todo el mundo. Un buen comienzo sería mirar esta página en wikipedia.
En cualquier caso, espero que te sirva de ayuda... Sé que no es una bala de plata, pero espero que al menos pueda orientarte en la dirección correcta.
Gracias por un artículo tan informativo y necesario. Me parece que la codificación regex es a menudo dado por sentado por las personas que lo saben, y que por lo general esperan que todo el mundo lo sabe también. Ahora tengo un recurso comprensible al que acudir.
Entiendo que controlar que los dígitos introducidos se ajusten a una norma preestablecida es una función destinada a minimizar los errores. En cuanto a los números de teléfono internacionales en un campo de formulario, sospecho que sería más fácil confiar en que el usuario compruebe que ha introducido el número de teléfono correcto que intentar microgestionar el campo para las muchas posibilidades. Por cierto, para mí el uso más importante de regex ha sido hasta ahora en el archivo .htaccess.
Aprecio que no sólo respondas a los comentarios en tu blog, sino que tus respuestas estén bien pensadas y tengan sentido. Buen trabajo.
Gracias Laurence.
Gran punto ... pero eso es parte de la belleza de Regex. Quiero decir que no es necesario utilizarlo en absoluto ... pero como he dicho en mi primera respuesta, usted podría hacer un regex extremadamente ligero que sólo obliga a introducir dígitos y guiones o algo así.
Sí, el uso de Regex con la directiva RedirectMatch es extremadamente útil ... hace que sea tan fácil de mapear estructuras url. Es especialmente importante usar esto cuando estás migrando un sitio donde todos los enlaces van a cambiar... Quiero decir que si no usas algún tipo de mapeo de url como RedirectMatch tu SEO puede sufrir dramáticamente al migrar tu sitio.
Blair,
Gracias por este post. Siempre evito expresiones regulares porque nunca he tomado el tiempo para entender la sintaxis. Esto realmente lo rompe y lo hace simple.
Ya no me dan miedo las expresiones regulares 🙂 .
¡Impresionante Jamie! Me alegro de haber sido de ayuda.