Ya conoces lo básico: las clases de caracteres como \d y [A-Z],
los cuantificadores como {4} y +, y las palabras matches?,
all-matching-subseqs y re-replace. Este ejercicio añade las partes
de las expresiones regulares que describen estructura y contexto.
Viven en el mismo vocabulario regexp, escritas con el mismo
literal R/ /.
Las anclas coinciden con una posición en lugar de con un carácter:
^ ! the start of the string
$ ! the end of the string
\b ! a word boundary
Como $ significa el final del string, escribe \$ cuando quieras
decir un signo de dólar literal.
Un límite de palabra (\b) es el punto en el que un carácter de
palabra se encuentra con uno que no lo es. Los caracteres de palabra
son las letras, los dígitos y el guion bajo; todo lo demás (los
espacios, la puntuación y los dos extremos del string) cuenta como
límite. Así, \bcat\b coincide con cat solo cuando aparece por sí
solo:
USING: regexp ;
"cat nap" R/ \bcat\b/ re-contains? . ! => t
"scatter" R/ \bcat\b/ re-contains? . ! => f (cat sits inside a word)
Los paréntesis ( ) agrupan parte de un patrón para que un
cuantificador (o un |, «esto o aquello») se aplique a todo el
grupo:
USING: regexp ;
"abab" R/ (ab)+/ matches? . ! => t (+ applies to "ab")
"dog" R/ cat|dog/ matches? . ! => t (either word)
"$100.50" R/ \$\d+(\.\d{2})?/ matches? . ! => t
"$100" R/ \$\d+(\.\d{2})?/ matches? . ! => t (the group is optional)
El último patrón se lee «un signo de dólar, uno o más dígitos y, después, un grupo opcional formado por un punto y dos dígitos».
Nota: en Factor, un grupo solo estructura el patrón. A diferencia de otras herramientas de expresiones regulares, no puedes recuperar el texto con el que ha coincidido un grupo: no hay extracción de grupos de captura y las retrorreferencias como
\1no son compatibles. Para extraer texto alrededor de una referencia, usa en su lugar la lookaround (más abajo).
Una lookaround comprueba lo que hay justo antes o justo después del punto actual sin que esos caracteres formen parte de la coincidencia. Tiene «ancho cero»: lo que comprueba no se incluye en el resultado.
(?=...) ! lookahead: ... must follow
(?!...) ! negative lookahead: ... must NOT follow
(?<=...) ! lookbehind: ... must come before
(?<!...) ! negative lookbehind: ... must NOT come before
La lookaround es selectiva: los mismos caracteres coinciden o no en
función de lo que tengan al lado. Un simple \d+ captura todos los
números; el lookbehind y el lookahead conservan solo los que tienen el
vecino adecuado:
USING: regexp ;
"3 for $10 or 10 for $30" R/ \d+/ all-matching-subseqs .
! => { "3" "10" "10" "30" } (every number)
"3 for $10 or 10 for $30" R/ (?<=\$)\d+/ all-matching-subseqs .
! => { "10" "30" } (only the prices — the bare 3 and 10 drop)
"buy 2 at 15% off, 4 at 30% off" R/ \d+(?=%)/ all-matching-subseqs .
! => { "15" "30" } (only the discounts — the bare 2 and 4 drop)
Añade una letra después de la / de cierre para cambiar el
comportamiento de todo el patrón. i hace que no distinga entre
mayúsculas y minúsculas:
USING: regexp ;
"REFUND" R/ refund/i matches? . ! => t
"Refund" R/ refund/i matches? . ! => t
all-matching-subseqs te da el texto coincidente. map-matches va un
paso más allá: ejecuta una quotation en cada coincidencia y recoge los
resultados. La quotation recibe el índice start de la coincidencia, su
índice end y el string completo; subseq convierte esos tres
elementos en el texto coincidente, que después transformas:
map-matches ( string regexp quot: ( start end string -- obj ) -- seq )
USING: math regexp sequences ;
"a1 b22 c333" R/ \d+/ [ subseq length ] map-matches .
! => { 1 2 3 } (the length of each run of digits)
Su prima each-match, que solo produce efectos
secundarios, ejecuta una quotation en cada coincidencia sin recoger
nada.
Llevas las cuentas de una pequeña empresa y cada día las transacciones llegan como líneas de texto desordenadas. Usarás expresiones regulares para validar importes, extraer cifras a partir del símbolo que las marca y marcar las líneas que necesitan un repaso más detenido.
Un importe bien formado consta de un $, uno o más dígitos y una parte de céntimos opcional formada por un punto seguido de exactamente dos dígitos: $100 o $100.50, pero no $100.5. Define valid-amount?, que devuelve t cuando el string entero es un importe bien formado.
"$100.50" valid-amount? . ! => t
"$100" valid-amount? . ! => t
"$100.5" valid-amount? . ! => f
Define dollar-amounts, que devuelve todos los números que siguen directamente a un $ en la línea (solo los dígitos, sin el $), en orden.
"spent $100 and $25 today" dollar-amounts .
! => { "100" "25" }
Define percentages, que devuelve todos los números a los que sigue inmediatamente un %.
"up 5% then down 12%" percentages .
! => { "5" "12" }
Define flagged?, que devuelve t cuando la línea menciona refund o chargeback con cualquier combinación de mayúsculas y minúsculas.
"Issued a REFUND today" flagged? . ! => t
"a normal sale" flagged? . ! => f
Regístrate en Exercism para aprender y dominar Factor con 47 conceptos163 ejercicios y mentoría humana real, todo gratis.