Introducción a las expresiones regulares.

Índice

Conceptos

  • Una expresión regular define un conjunto de una o más cadenas de caracteres.
  • Consisten en la combinación de caracteres alfanuméricos y metacaracteres (caracteres especiales).
  • Un patrón se utiliza para encontrar coincidencias en cadenas de texto.
  • Una cadena de caracteres es en sí una expresión regular que define una cadena de caracteres: a sí misma. Un caracter es cualquier cosa ASCII menos \n (retorno de carro, fin de línea, ENTER.
  • El resultado de un coincidencia es tanto la coincidencia como la no coincidencia. Incluso cuando se produce una coincidencia no es necesario que coincida todo el patrón (esto se explicará más adelante).
  • Una expresión regular se corresponde con la cadena más larga empezando lo más ceca posible del principio de la línea (a esto se le llama correspondencia exhaustiva según perl).
  • Las expresiones regulares se usan de tres formas: coincidencia, reeemplazo y división (search and replace and splitting). La última es básicamente lo mismo que la coincidencia reversa (esto es, todo lo que no coincide).
  • A menudo se llaman simplemente regexp o RE

Debido a su versatilidad, las expresiones regulares son ampliamente utilizadas en el procesamiento de texto y el análisis. Para los usuarios de UNIX son probablemente bastante familiares debido al uso de los programas grep, sed, awk y ed y editores de texto como (X) Emacs y vi/vim. Probablemente el uso más conocido de las expresiones regulares es en el lenguaje de programación Perl, que utiliza la aplicación de expresiones regulares más avanzada a día de hoy.

Motivos

Probablemente se preguntará porqué debería aprender a utilizar expresiones regulares. Si usted es un usuario normal quizá los beneficios que le aporte el conocer las expresiones regulares no sean demasiados, en cambio, si usted es o va a ser un desarrollador o un administrador de sistemas las expresiones regulares le harán la vida más fácil.

Los desarrolladores pueden utilizarlas para analizar archivos de texto, modificar código y otras maravillas. Los administradores de sistemas pueden usarlas para automatizar tareas aburridas, manejar logs y monitorizar el tráfico de la red en busca de actividades no autorizadas.

Delimitadores

  • Normalmente marcan el principio y el fin de las expresiones regulares. Siempre es un caracter especial.
  • Vim permite el uso de otros caracteres como delimitadores.
  • grep no utiliza delimitadores
  • Generalmente se usa / y en algunos casos no será necesario al final de la expresión regular si esta va seguida inmediatamente de nueva línea.

Cuantificadores

Antes de empezar a explicar la sintaxis sería conveniente que se asegurara de disponer de los siguientes programas para probar los ejemplos mostrados en este artículo. Bastará con tener instalados sed, gawk y grep.

Como se explicó antes una expresión regular consiste en una serie de caracteres alfanuméricos y metacaracteres. Un caracter alfanumérico será una letra del alfabeto o un número. En la actualidad, en el mundo de las expresiones regulares, cualquier caracter que no sea un metacaracter conincidirá consigo mismo ( a menudo llamados caracteres literales, de todas formas, la mayoría del tiempo usted trabajará con caracteres alfanuméricos.

Un metacaracter muy especial es la barra invertida o backslash, ésta convierte cualquier metacaracter en un caracter literal.

Los metacaracteres son:

\ | ( ) [ { ^ $ * + ? . < >

y se utilizan para hacer que las expresiones regulares coincidan con más de una cadena. Una expresión regular siempre concuerda con la cadena más larga posible, emèzando por la más cercana al principio de la línea ( a la izquierda).

Y sin perder el tiempo vamos a ver como se comporta el primer metacaracter. El punto (.) necesita alguna explicación porque su uso en expresiones regulares a menudo puede resultar confuso.

La marca de puntuación no lleva a encontrar coincidencias con la puntuación de cualquier línea, es un metacaracter que coincidirá con cualquier caracter. Si se quieren encontrar coincidencias con su significado literal es necesario utilizar la barra invertida (backslashificarlo, que dicen algunos textos en inglés).

1.23

coincidirá con el número 1.23 pero también con las siguientes líneas

1x23
1 23
1-23

para hacer que la expresión sólo coincida con el número flotante tenemos que cambiar la expresión a

1\.23

Recordando que esto es muy importante podemos continuar.

Dos importantes metacaracteres son * y +. Se llaman cuantificadores y le dicen a la máquina que busque varias coincidencias del patrón en una fila. * coincidirá con cero o más apariciones del patrón, + es similar, sólo que coincidirá con una o más apariciones. ¿Qué ocurriría si usted, que quiere buscar palabras que tienen el caracter c, tiene la tentación de escribir algo como esto?

sh-3.00# ls > ls.txt
sh-3.00# grep -E "c*" ls.txt

Quizá se lleve la sorpresa de que se encuentran una gran cantidad de coincidencias, incluso con palabras que no contienen la letra c. ¿Cómo es eso? La respuesta es simple. Recordemos que el carácter * coincide con cero o más caracteres, y eso es exactamente lo que hizo, coincidir con cero caracteres.
Se puede ver que las expresiones regulares tienen la posibilidad de coincidir con lo que se conoce como cadena vacía, que es simplemente una cadena con tamaño cero. Esta cadena vacía en realidad se puede encontrar en todos los textos, por ejemplo, la palabra:

go

contiene tres cadenas vacías, están situadas antes de la g, entre la g y la o, y después de la o. Y una cadena vacía contiene exactamente una cadena vacía. Al principio esto puede parecer algo tonto, pero más adelante se verá cómo se puede usar en expresiones más complejas.

Por eso, con esto que sabemos, podemos cambiar nuestra expresión por:

sh-3.00# grep -E "c+" ls.txt

Con lo que obtendremos el resultado que esperábamos al principio.

El siguiente metacaracter es ?, que, simplemente le dirá a la máquina que encuentre un caracter o no ( cero o uno). Por ejemplo, la expresión.

cow?

coincidirá con cualquiera de estas líneas.

cow
cows

Un cuantificador más generalizado con tres metacaracteres sería

{n,m}

donde m y n son respectivamente el tamaño menor y mayor del cuantificador. Por Ejemplo

{1,5}Los cuantificadores sólo trabajan con el caracter que tienen a la izquierda así que esto limita bastante nuestras expresiones. Si los usamos con metacaracteres nos encontraremos con que abrimos un amplio abanico de posibilidades.

signigfica que el patrón coincidirá con 1 a 5 caracteres. Para realizar una búsqueda hasta un número infinito de caracteres bastará con hacer algo así:

{1,}

lo que encontrará coincidencias con uno o más caracteres. Esto es justo lo mismo que hace el caracter +. Se pueden ver las relaciones:

* ------------> {0,}
+ ------------> {1,}
? ------------> {0,1}

{5} hará que se busquen coincidencias con cinco caracteres, ni más ni menos.

Una salida de terminal puede aclarar un poco las cosas.

sh-3.00# grep -E "c{2,}" ls.txt
ccalc.c
sh-3.00# grep -E "{2,}c" ls.txt
calc_fx.py
calc.py
ccalc.c
Choices
hola.c
icewm
my-applications
my-documents
puppy-reference

Afirmaciones

La siguiente clase de metacaracteres son las afirmaciones. Éstas hallaran coincidencias si una afirmación dada es cierta. Las primeras son ^ y & , que buscarán el principio y el fin de la línea respectivamente. Estas aserciones siempre encuentran una cadena de longitud cero, lo que equivale a buscar una posición.

sh-3.00# grep -E "il$" ls.txt
sh-3.00# grep -E "^Ma" ls.txt

Las dos búsquedas encontrarán la línea que dice Mail en ese archivo ls.txt

La siguiente afirmación o aserción alcanzan o coinciden con el principio o el final de una línea, estas son > y <.

La salida estandar se explica mejor que mil palabras

sh-3.00# grep -E "cow" ls.txt
cow
cowage
coward
cowboy
cowl
sh-3.00# grep -E "cow\>" ls.txt
cow
sh-3.00#

Grupos y alternancias.

Utilizaremos \( y \) para poner entre paréntesis una expresión regular (no así en perl que no utiliza las barras invertidas en este caso).

La cadena que se corresponde con la expresión regular entre paréntesis puede volverse a llamar más adelante.

Una expresión regular no intenta encontrar correspondencias con un paréntesis acotado ("backslashificado"). /\(regexp\)/ se corresponde con la expresión con la que se correspondería /regexp/.

Podemos anidar paréntesis acotados. Las expresiones entre paréntesis sólo se identifican por su apertura \(, por lo que no se produce ambigüedad al identificarla.


Si tenemos algo así:            3 4 red ?., ; f  NSOERFOSA rsdi 223 1 d

La expresión regular:                      \([a-z] \([A-Z]*\) r\)  
                                               |      /\    / 
Nos debería coincidir con:                     f NSOERFOSA r

El caracter | es un operador booleano. En vim tendremos que acotarlo para hacerlo un caracter especial. Una bara vertical entre dos expresiones regulares producirá una correspondencia con cadenas que se correspondan con la primera expresión, la segunda o ambas. Se puede utilizar paréntesis para separar el resto de la expresión regular de las dos expresiones a las que se aplica.

/(Sr|Sra)\. [A-Z][a-z]*/

Secuencias.

  • Definen una clase de caracteres que se corresponde con cualquier caracter único entre corchetes
  • Si el primer caracter entre los corchetes es ^ se corresponderá con cualquier caracter único que no esté entre los corchetes.
  • Con un guión definimos un rango de caracteres.
  • Dentro de una definición de clase las barras invertidas (backslash) y los asteriscos pierden su significado especial.
  • Un corchete de cierre como miembro de una clase sólo puede aparecer como primer miembro de la clase después del corchete de apertura.
  • Un acento circunflejo es especial sólo si es el primer caracter que aparece después del corchete de apertura.
  • Un signo de dólar ($) es especial sólo si es el último caracter antes del corchete de cierre.

El operador *

Coincide con cero o más ocurrencias de la correspondencia de la expresión regular.


Expresión                             Coincidencias

/ab*c/                                ac, abc, abbc, zxabrhabbbbch
/ab.*c/                               paraelcarroababsoluterockandrollclassictralala
/\(.*\)/                              (esto) y (esto otro)
/\([^\)]*\)/                          (esto) y (esto otro)

Referencias

http://www.zez.org/article/articleview/11/1/
http://roxterm.sourceforge.net/
Manual práctico de Linux. Comandos, editores y programación Shell. Mark G. Sobell. ISBN: 978-84-415-2741-6

Enlaces para saber más.

http://es.wikipedia.org/wiki/Expresi%C3%B3n_regular
http://bulma.net/body.phtml?nIdNoticia=770&nIdPage=2
http://javascripts.astalaweb.com/Ayuda/html/js56reconquantifiers.asp
http://www.regular-expressions.info/reference.html
https://addons.mozilla.org/es-ES/firefox/addon/2077/
http://www.visibone.com/regular-expressions/
http://www.gnu.org/manual/gawk/html_node/Regexp.html
http://www.gnu.org/software/emacs/manual/html_node/emacs/Regexps.html
http://www.delorie.com/gnu/docs/regex/regex_toc.html
http://www.cs.utah.edu/dept/old/texinfo/regex/regex_toc.html
http://www.grymoire.com/Unix/Regular.html
http://www.desarrolloweb.com/manuales/expresiones-regulares.html
http://javascript.espaciolatino.com/lengjs/jsgram/expregulares.htm
http://www.elcodigo.net/tutoriales/jsavanzado/jsavanzado5.html
http://www.bdat.net/documentos/expresiones_regulares/book1.html
http://delta.cs.cinvestav.mx/~gmorales/ta/node72.html