Bienvenido a los foros de Perl en Español, carlosss.
El asunto es algo complejo, ya que los separadores son a su vez separadores dentro de los campos.
Hay que utilizar técnicas de eliminación del
backtraking para, en el caso del DNI, quedarnos con el mejor caso posible.
Otro tema curioso es que cada línea tenga dos registros, pero bueno... cosas más raras hemos visto, ¿no?
La siguiente solución me funciona con los ejemplos que he colocado en la sección DATA. Supongo que habrá mejores formas de hacer lo mismo.
Using perl Syntax Highlighting
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
## Definimos la gramática
my $sep = qr/ {3,}/;
my $texto = qr/((?:\w| {0,2}(?=\w))+)/;
my $nombre = $texto;
my $dni = qr/( {9}?| {0,8}\w{1,9})/;
my $ident = qr/(\w\w +.{9} +\w\w?)/;
my $color = $texto;
my $localidad = $texto;
my $registro = qr/$nombre $sep $dni $sep $ident $sep $color $sep $localidad/x;
## Procesamos
while (<DATA>) {
chomp;
/^ $registro $sep $registro /x; # dos registros separados por cada línea
next if $4 ne 'AMARILLO' and $4 ne 'ROJO'; # solo nos interesan dos colores
print "[$1][$2][$3][$4][$5]\n"; # pintamos lo encontrado
next if $9 ne 'AMARILLO' and $9 ne 'ROJO'; # lo mismo, para el segundo registro
print "[$6][$7][$8][$9][$10]\n";
}
# NOMBRE DNI IDENTIFICADOR COLOR LOCALIDAD NOMBRE DNI IDENTIFICADOR COLOR LOCALIDAD
__DATA__
RAUL GARCIA RODRIGUEZ Z4385964 W3 436547873 0 AZUL CLARO MIRANDA DE EBRO RAUL GARCIA RODRIGUEZ Z4385964 W3 436547873 0 AZUL CLARO MIRANDA DE EBRO
CARLOS SANZ 43645342 43 324543453 34 AMARILLO SEVILLA LUIS ANTONIO RUIZ 33 323KJ5434 5 AMARILLO VALENCIA
LUIS ANTONIO RUIZ 33 323KJ5434 5 AMARILLO VALENCIA LUIS ANTONIO RUIZ 33 323KJ5434 5 AMARILLO VALENCIA
Coloreado en 0.005 segundos, usando
GeSHi 1.0.8.4
En el caso del DNI, ponemos que, primero intente localizar 9 blancos. Y que si los encuentre, no vuelva hacia atrás para intentar acomodar el resto de campos. Eso se consigue con el '?' que le sigue. De todas maneras, la conjunción de campos de separación con el resto de campos ayuda a 'encajar' el campo de DNI vacío entre el mar de blancos (DNI rodeado por separadores también blancos).
La explicación de las expresiones regulares es la siguiente:
*
$sep = qr/ {3,}/; Los separadores son 3 o más espacios
*
$texto = qr/((?:\w| {0,2}(?=\w))+)/; Un texto de un campo cualquiera será un conjunto de caracteres alfanuméricos y/o blancos, pero estos últimos deben estar seguidos por otro carácter alfanumérico. Se podría simplificar en
qr/(\b[\w ]+\b)/, pero habría un ligero retraso por
backtracking con el siguiente separador (le hemos "comido" todos sus blancos)
*
$nombre = $texto;*
$color = $texto;*
$localidad = $texto; Estos tres campos contienen la misma expresión regular
*
$dni = qr/( {9}?| {0,8}\w{1,9})/; Un DNI son 9 blancos; o un conjunto de entre 0 y 8 blancos seguidos por un conjunto de entre 1 y 9 alfanuméricos. Si hay 9 blancos, terminamos enseguida '?', y solo ocurre
backtracking para liberar espacios para el separador que nos sigue y cogerlos del precedente
*
$ident = qr/(\w\w +.{9} +\w\w?)/; Una expresión regular normal
*
$registro = qr/$nombre $sep $dni $sep $ident $sep $color $sep $localidad/x; La unión de todos los demás