Asignatura: Entornos de programación

Expresiones regulares
Herramientas Grep y AWK


En este tema se introducen las expresiones regulares y un par de utilidades que las usan de manera intensiva: 'grep' y 'awk'. Estas utilidades proceden del mundo UNIX, y están disponibles hoy día en todas las plataformas usadas habitualmente.


Contenido

  1. Expresiones regulares
    1. Expresiones simples
    2. Expresiones compuestas
    3. Ejemplos de expresiones regulares
  2. Herramienta Grep
  3. Lenguaje AWK
    1. Elementos básicos
    2. Ejemplo: recuento de notas
    3. Patrones
    4. Operadores
    5. Funciones predefinidas
    6. Campos en las líneas de entrada
    7. Variables simples
    8. Variables simples predefinidas
    9. Acciones
    10. Vectores o tablas (arrays)
    11. Vectores (arrays) predefinidos
    12. Vectores con varios índices (seudomatrices)
    13. Funciones definidas por el usuario
    14. Invocar un programa AWK
  4. Ejemplo: vocabulario usado en un texto

Expresiones regulares

Las expresiones regulares permiten describir estructuras sintácticas, en particular las de los lenguajes regulares. Son, por tanto, patrones sintácticos a los que deben ajustarse los elementos que componen el vocabulario del lenguaje.

Dicho de una manera más sencilla, las expresiones regulares son patrones que permiten reconocer secuencias de símbolos con una estructura sintáctica determinada.

Hay diversas notaciones para representar expresiones regulares. Por ejemplo, la notación BNF y otras derivadas de ella. Los comodines (wildcards) usados para designar conjuntos de ficheros vienen a ser una forma restringida de rexpresiones regulares.

La notación que se describe aquí permite escribir patrones para secuencias de caracteres (strings), y corresponde a la que con ligeras variantes se usa en diversas utilidades para proceso de textos: "grep", "awk", "perl", la librería "regexp", etc. Los apartados siguientes describen los elementos principales. Para una descripción más completa se debe consultar el manual de referencia correspondiente.

Expresiones simples

Son patrones que se ajustan a un único símbolo (en general, a un único carácter):

Expresión Significado
x carácter x, si es carácter normal
. cualquier carácter
[aeiou] un carácter del conjunto
[a-z] un carácter del rango
[^aeiou0-9] complementa el conjunto
\x carácter x, si x es un carácter especial
^ principio del texto, si va al comienzo
$ fin del texto, si va al final
\< principio de palabra (*)
\> fin de palabra (*)

(*) Patrón no estándar

Expresiones compuestas

Son patrones que combinan expresiones simples. Se ajustan a una secuencia de símbolos.

Expresión Significado
xy expresión x seguida de y
x+ una o más repeticiones de x
x* cero o más repeticiones de x
x? cero o una aparición de x
una|otra una u otra expresión
(x) expresión x

Ejemplos de expresiones regulares

Herramienta Grep

Es un programa de utilidad que permite seleccionar las líneas de un texto que cumplen con un patrón determinado. La orden para invocarla es:

> grep patrón ficheros...

El patrón se escribe como una expresión regular. Los nombres de los ficheros de entrada pueden incluir comodines. El programa lee los ficheros y copia a la salida cada línea que se ajuste al patrón.

La utilidad grep procede de UNIX. Dispone de parámetros opcionales para controlar su funcionamiento. Para conocer las posibles opciones se puede invocar como:

> grep --help
> grep -h
> grep

Ejemplo: extraer todas las líneas de código C/C++ que empiecen con un comentario (// o /*) desde la primera posición.

> grep ^/[/\*] *.h *.c *.cpp

Lenguaje AWK

AWK es un lenguaje para procesar ficheros de texto, línea a línea. Resulta apropiado para extraer datos individuales, realizar recuentos, modificar el formato de los datos, generar resúmenes, etc. La potencia del lenguaje reside en el uso extensivo de expresiones regulares para seleccionar los fragmentos de información apropiados, y la posibilidad de combinar los estilos de programación declarativo e imperativo.

En realidad existen distintas variantes de AWK, dependiendo del procesador utilizado:

En los ejemplos se usará gawk.

Elementos básicos

Ejemplo: recuento de notas

Para dar una idea rápida de cómo es un programa en AWK, a continuación se presenta como ejemplo el proceso de una lista de calificaciones de un examen para obtener un resumen estadístico simple. El significado de los elementos que aparecen en el programa se irá viendo en los siguientes apartados.

Fichero de datos - notas de un examen ( DNI del alumno y nota numérica):

051422949  4.3
051943388  8
005428776  7.5
052970557
052375629  3
002550123  9.5
014301873
050100456  6
079309554  5.5
002915589
008928257  8.7

Programa - recuento de aprobados y suspensos:

$1 {alumnos++}

$2 >= 5 {aprobados++}

NF==1 {nopresentados++}

END {
  suspensos = alumnos - aprobados - nopresentados
  print "Aprobados:     ", aprobados
  print "Suspensos:     ", suspensos
  print "No presentados:", nopresentados
  print "Total alumnos: ", alumnos
}

Resultados:

Aprobados:      6
Suspensos:      2
No presentados: 3
Total alumnos:  11

Patrones

En AWK hay varias clases de patrones. Todos ellos funcionan como condiciones con un resultado booleano.

Operadores

Como se ha dicho, son similares a los del lenguaje C. Los más importantes son:

Los valores numéricos y de texto son convertidos automáticamente de un tipo a otro cuando sea necesario. Cuando se quiera forzar una conversión se puede escribir:

Funciones predefinidas

Entre otras, las siguientes:

Campos en las líneas de entrada

Cada línea de texto de la entrada se descompone automáticamente en campos. Por defecto, los campos se separan por espacio en blanco. Los campos pueden referenciarse por separado, mediante el operador $.

Ejemplo:

Variables simples

Una variable simple puede contener un valor numérico o de texto. Las variables no se declaran, simo que empiezan a existir cuando se usan por primera vez. Inicialmente tienen valor nulo.

Variables simples predefinidas

Existen variables predefinidas que sirven de comunicación entre el código del usuario y el propio intérprete de AWK. Entre otras las siguientes:

Las variables anteriores toman valor automáticamente con cada línea. Otras variables son asignadas por el usuario para controlar el funcionamiento del intérprete. Por ejemplo:

Acciones

Las acciones son sentencias o secuencias de sentencias que se escriben como en lenguaje C. El código de una sentencia termina implícitamente con el fin de línea o explícitamente con punto y coma (;). Algunas sentencias de uso frecuente son:

Sentencia Significado
var = expresión asignación de valor a una variable
expresión evaluación de la expresión (la asignación anterior es realmente un caso particular de expresión, usando el operador de asignación)
if (condición) acción acción condicional
if (condición) acción else acción acciones alternativas
while (condición) acción bucle WHILE
for (k=ini; k<=fin; k++) acción-con-k bucle FOR (como en C)
{ sentencia; sentencia ... } acción compuesta
print expresión, expresión ... imprime valores separados por espacio en blanco
print equivale a print $0
printf( formato, expresión, expresión ... ) impresión con formato (como en C)
print ... > fichero redirige la salida al fichero
getline variable < fichero lee explícitamente una línea de un fichero
close( fichero ) libera el fichero
next termina el proceso de la línea de entrada actual
exit termina la lectura de la entrada y da control a las acciones END (o bien termina definitivamente)

Vectores o tablas (arrays)

La única estructura de datos que existe en AWK son los vectores o tablas (en inglés array). También se les llama vectores asociativos. Tienen las siguientes características

Vectores (arrays) predefinidos

Existen algunos vectores predefinidos que permiten acceder a información del ambiente de ejecución:

Vectores con varios índices (seudomatrices)

Se pueden simular estructuras matriciales de dos o más dimensiones refundiendo varios índices en uno solo. Existe una variable predefinida SUBSEP cuyo contenido se usa implícitamente como delimitador:

Funciones definidas por el usuario

Además de las funciones predefinidas el usuario puede definir sus propias funciones. La definición de una función tiene el mismo aspecto que una cláusula o regla del programa, usando un patrón especial que empieza con la palabra clave function.

Invocar un programa AWK

Los programas en AWK se ejecutan bajo intérprete. Por lo tanto para usar un programa hay que invocar el intérprete correspondiente, indicando cuál es el programa AWK a ejecutar y cuáles son los ficheros de datos de entrada. El formato de la orden es uno de los siguientes:

> gawk  "programa"  fichero fichero ...
> gawk  -f programa  fichero fichero ...

El primer caso sólo es útil para programas muy cortos. El texto del programa se escribe literalmente, entre comillas, en la propia orden, antes de los nombres de los ficheros de entrada. En el segundo caso el programa se almacena en un fichero de texto y el nombre de dicho fichero se indica con la opción -f. La orden puede incluir también una asignación de valores iniciales a ciertas variables del programa, con la opción -v:

> gawk  ... -v variable=valor ...

El siguiente ejemplo usa "awk" para emular el funcionamiento de la herramienta "grep":

> gawk  "/^[0-9]/" *.txt

Esta orden imprime (acción por defecto) cada línea que empiece (^) por un carácter numérico (0-9) en cualquier fichero de texto (*.txt) que haya en el directorio actual.

El nombre de la orden debe corresponderse con el intérprete concreto que se utilice. Hay distintos intérpretes para distintas variantes de AWK: awk, nawk, mawk, gawk, tawk, etc.

Ejemplo: vocabulario usado en un texto

Este programa imprime la lista de todas las palabras diferentes usadas en un texto, junto con la frecuencia de aparición de cada una. Para simplificar el programa se asume que no hay signos de puntuación, y las palabras están separadas por espacio en blanco o saltos de línea. Esto significa que cada palabra es un campo de la línea de entrada.

Código del programa (vocabulario.awk):

{
    for (k=1; k<=NF; k++) {
        cuenta[$k]++
    }
}

END {
    for (pal in cuenta) {
        print pal, cuenta[pal]
    }
}

Observaciones:

  1. La primera cláusula del programa tiene el patrón omitido. Se aplica a todas las líneas del texto de entrada.
  2. Las palabras en mayúsculas y en minúsculas se tratan como diferentes. Este es el comportamiento por defecto de AWK.
  3. Los resultados no aparecen en orden alfabético (ni en ningún otro orden determinado). Las tablas suelen implementarse internamente como tablas "hash", y sus elementos se recorren en el orden que haya decidido quien construyó el intérprete.

Ejemplo de texto de entrada (texto.txt):

Este programa imprime la lista de todas las
palabras diferentes usadas en un texto, junto con
la frecuencia de aparición de cada una. Para
simplificar el programa se asume que no hay
signos de puntuación, y las palabras están
separadas por espacio en blanco o saltos de
línea. Esto significa que cada palabra es un
campo de la línea de entrada.

Orden de ejecución:

> gawk -f vocabulario.awk texto.txt

Resultados:

entrada. 1
Esto 1
no 1
frecuencia 1
palabra 1
el 1
con 1
palabras 2
todas 1
lista 1
imprime 1
programa 2
línea 1
Para 1
campo 1
que 2
... etc. ...