Subsecciones


Secuencias de números de tamaño fijo

El siguiente problema y sus soluciones se describen en el libro de J.E.F. Friedl [1]. Supongamos que tenemos un texto conteniendo códigos que son números de tamaño fijo, digamos seis dígitos, todos pegados, sin separadores entre ellos, como sigue:

012345678901123334234567890123125934890123345126

El problema es encontrar los códigos que comienzan por 12. En negrita se han resaltado las soluciones. Son soluciones sólo aquellas que, comienzan por 12 en una posición múltiplo de seis. Una solución es:

@nums = grep {m/^12/} m/\d{6}/g;

que genera una lista con los números y luego selecciona los que comienzan por 12. Otra solución es:

@nums = grep { defined } m/(12\d{4})|\d{6}/g;

que aprovecha que la expresión regular devolverá una lista vacía cuando el número no empieza por 12:

DB<1> $x = '012345678901123334234567890123125934890123345126'
DB<2> x  ($x =~ m/(12\d{4})|\d{6}/g)
0  undef
1  undef
2  123334
3  undef
4  undef
5  125934
6  undef
7  undef
Obsérvese que se esta utilizando también que el operador | no es greedy.

¿Se puede resolver el problema usando sólamente una expresión regular? Obsérvese que esta solución ``casi funciona'':

 DB<3> x @nums = $x =~ m/(?:\d{6})*?(12\d{4})/g;
0  123334
1  125934
2  123345

recoge la secuencia mas corta de grupos de seis dígitos que no casan, seguida de una secuencia que casa. El problema que tiene esta solución es al final, cuando se han casado todas las soluciones, entonces la búsqueda exhaustiva hará que nos muestre soluciones que no comienzan en posiciones múltiplo de seis. Por eso encuentra 123345:

012345678901123334234567890123125934890123345126
Por eso, Friedl propone esta solución:

@nums = m/(?:\d{6})*?(12\d{4})(?:(?!12)\d{6})*/g;

Se asume que existe al menos un éxito en la entrada inicial. Que es un extraordinario ejemplo de como el uso de paréntesis de agrupamiento simplifica y mejora la legibilidad de la solución. Es fantástico también el uso del operador de predicción negativo.

Solución usando el ancla $ \backslash$ G

El ancla \G ha sido concebida para su uso con la opción /g. Casa con el punto en la cadena en el que terminó el último emparejamiento. Cuando se trata del primer intento o no se está usando /g, usar \G es lo mismo que usar \A.

Mediante el uso de este ancla es posible formular la siguiente solución al problema planteado:

pl@nereida:~/Lperltesting$ perl -wde 0
main::(-e:1):   0
DB<1> $_ = '012345678901123334234567890123125934890123345126'
DB<2> x m/\G(?:\d{6})*?(12\d{4})/g
0  123334
1  125934

Sustitución

Si lo que se quiere es sustituir las secuencias deseadas es poisble hacerlo con la siguiente expresión regular:

casiano@nereida:~/docs/curriculums/CV_MEC$ perl -wde 0
DB<1> x $x = '012345678901123334234567890123125934890123345126'
0  012345678901123334234567890123125934890123345126
DB<2> x  ($y = $x) =~ s/(12\d{4})|\d{6}/$1? "-$1-":$& /ge
0  8
DB<3> p $y
012345678901-123334-234567890123-125934-890123345126

Casiano Rodríguez León
2009-12-09