regex - regular - “La longitud de la variable no está implementada”, pero no es la longitud variable
regex one (4)
Tengo un regex muy loco que estoy tratando de diagnosticar. También es muy largo, pero lo he reducido a la siguiente secuencia de comandos. Ejecutar utilizando Strawberry Perl v5.26.2.
use strict;
use warnings;
my $text = "M Y H A P P Y T E X T";
my $regex = ''(?i)(?<!(Mon|Fri|Sun)day |August )abcd(?-i)'';
if ($text =~ m/$regex/){
print "true/n";
}
else {
print "false/n";
}
Esto da el error "El aspecto de longitud variable no se implementó en expresiones regulares".
Espero que pueda ayudar con varios problemas:
- No veo por qué se produciría este error, porque todos los valores posibles de mirar atrás son 7 caracteres: "Lunes", "Viernes", "Domingo", "Agosto".
-
Yo no escribí esta expresión regular, y no estoy seguro de cómo interpretar la sintaxis
(?i)
y(?-i)
. Cuando me deshago de(?i)
el error desaparece. ¿Cómo interpretará Perl esta parte de la expresión regular? Pensaría que los dos primeros caracteres se evalúan como "paréntesis literales opcionales" excepto que no se escapan los paréntesis y, en ese caso, obtendría un error de sintaxis diferente porque los paréntesis de cierre no coincidirían. - Este comportamiento comienza en algún lugar entre Perl 5.16.3_64 y 5.26.1_64, al menos en Strawberry Perl. La versión anterior está bien con el código, la última no. ¿Por qué comenzó?
Eso es porque
st
puede ser una ligadura.
Lo mismo sucede con
fi
y
ff
:
#!/usr/bin/perl
use warnings;
use strict;
use utf8;
my $fi = ''fi'';
print $fi =~ /fi/i;
Entonces imagine algo como
fi|fi
, donde, de hecho, la longitud de las alternativas no es lo mismo.
He reducido tu problema a esto:
my $text = ''M Y H A P P Y T E X T'';
my $regex = ''(?<!st)A'';
print ($text =~ m/$regex/i ? "true/n" : "false/n");
Debido a la presencia del modificador
/i
(no distingue mayúsculas y minúsculas) y la presencia de ciertas combinaciones de caracteres, como
"ss"
o
"st"
que pueden reemplazarse por una
Typographic_ligature
que tenga una longitud variable (
/August/i
coincide, por ejemplo, en
AUGUST
(6 caracteres) y
august
(5 caracteres, el último es U + FB06)).
Sin embargo, si eliminamos el modificador
/i
(no distingue mayúsculas y minúsculas), funciona porque las ligaduras tipográficas no coinciden.
Solución:
Utilice modificadores
aa
es decir:
/(?<!st)A/iaa
O en tu regex:
my $text = ''M Y H A P P Y T E X T'';
my $regex = ''(?<!(Mon|Fri|Sun)day |August )abcd'';
print ($text =~ m/$regex/iaa ? "true/n" : "false/n");
Desde perlre :
Para prohibir las coincidencias ASCII / no ASCII (como "k" con "/ N {KELVIN SIGN}"), especifique la "a" dos veces, por ejemplo
/aai
o/aia
. (La primera aparición de "a" restringe el/d
, etc., y la segunda aparición agrega las restricciones "/ i".) Pero, tenga en cuenta que los puntos de código fuera del rango ASCII utilizarán las reglas de Unicode para la coincidencia de/i
, por lo que el modificador realmente no restringe las cosas solo a ASCII; simplemente prohíbe la mezcla de ASCII y no ASCII .
Ponga
(?i)
después de mirar detrás de:
(?<!(Mon|Fri|Sun)day |August )(?i)abcd(?-i)
o
(?<!(Mon|Fri|Sun)day |August )(?i:abcd)
A mi me parece que es un error.
st
podría representarse en una
ligadura estilística de
1 carácter como
st
o
ſt
, por lo que su longitud podría ser 2 o 1.
Encontrar rápidamente la lista completa de perl de ligaduras de 2 → 1 caracteres mediante un comando bash:
$ perl -e ''print $^V''
v5.26.2
$ for lig in {a..z}{a..z}; do /
perl -e ''print if /(?<!''$lig'')x/i'' 2>/dev/null || echo $lig; done
ff fi fl ss st
Estos representan respectivamente las ligaduras
ff
,
fi
,
fl
,
ß
y
st
/
ſt
.
(
ſt
representa
ſt
, usando el
carácter largo s
obsoleto; coincide con
st
y
no
coincide con
ft
.)
Perl también es compatible con las ligaduras estilísticas restantes,
ffi
y
ffl
para
ffi
y
ffl
, aunque esto no es significativo en este contexto, ya que las miradas ya tienen problemas con
ff
y
fi
/
fl
separado.
Las versiones futuras de perl pueden incluir más ligaduras estilísticas, aunque todo lo que queda es específico de la fuente (p. Ej.,
Linux Libertine
tiene ligaduras estilísticas para
ct
y
ch
) o estrictamente estilísticas (como Dutch Dutch for
ij
o el obsoleto Spanish
ꝇ
for
ll
).
No parece apropiado tener este tratamiento para las ligaduras que no son totalmente intercambiables (nadie aceptaría por
dœs
), aunque existen otros escenarios, como la inclusión de
ß
gracias a que la
forma en mayúscula es
SS
.
Perl 5.16.3 (y, de manera similar, versiones antiguas) solo tropieza con
ss
(para
ß
) y no logra expandir las otras ligaduras en lookbehinds (tienen un ancho fijo y no coincidirán).
No busqué la corrección de errores para detallar exactamente qué versiones están afectadas.
Perl 5.14 introdujo el soporte de ligadura, por lo que las versiones anteriores no tienen este problema.
Soluciones
Soluciones para
/(?<!August)x/i
(solo la primera evitará correctamente
August
):
-
/(?<!Augus[t])(?<!Augu(?=st).)x/i
(absolutamente completo) -
/(?<!Augu(?aa:st))x/i
(solo elst
en el lookbehind es ² "ASCII-safe") -
/(?<!(?aa)August)x/i
(todo el aspecto por detrás es "ASCII-seguro" ²) -
/(?<!August)x/iaa
(toda la expresión regular es "ASCII-segura" ²) -
/(?<!Augus[t])x/i
(rompe la ligadura buscando ¹) -
/(?<!Augus.)x/i
(ligeramente diferente, coincide más) -
/(?<!Augu(?-i:st))x/i
(st
sensible a las mayúsculas y minúsculas en lookbehind, no coincidirá conAugusTx
)
Estos juguetes eliminan el modificador que no distingue entre mayúsculas y minúsculas ¹ o añaden el modificador seguro para ASCII ² en varios lugares, lo que a menudo requiere que el escritor de expresiones regulares sepa específicamente la ligadura de ancho variable.
La primera variación (que es la única completa) coincide con los anchos variables con dos miradas detrás: primero para la versión de seis caracteres (sin ligaduras como se indica en la primera cita a continuación) y la segunda para cualquier ligadura, empleando un lookahead
delantero
(que tiene cero) ancho!) para
st
(incluyendo las ligaduras) y luego contabilizando su ancho de un solo carácter con a
.
Dos segmentos de la perlre :
Mod Modificador insensible a las mayúsculas y minúsculas
Hay una serie de caracteres Unicode que coinciden con una secuencia de múltiples caracteres en
/i
. Por ejemplo, "LATIN SMALL LIGATURE FI" debe coincidir con la secuenciafi
. Perl actualmente no puede hacer esto cuando los múltiples caracteres están en el patrón y se dividen entre agrupaciones, o cuando se cuantifican uno o más. Así
"/N{LATIN SMALL LIGATURE FI}" =~ /fi/i; # Matches [in perl 5.14+] "/N{LATIN SMALL LIGATURE FI}" =~ /[fi][fi]/i; # Doesn''t match! "/N{LATIN SMALL LIGATURE FI}" =~ /fi*/i; # Doesn''t match! "/N{LATIN SMALL LIGATURE FI}" =~ /(f)(i)/i; # Doesn''t match!
² Modificador de seguridad ASCII
/aa
(perl 5.14+)
Para prohibir las coincidencias ASCII / no ASCII (como
k
con/N{KELVIN SIGN}
), especifique laa
dos veces, por ejemplo/aai
o/aia
. (La primera aparición dea
restringe/d
, etc., y la segunda agrega las restricciones/i
.) Pero tenga en cuenta que los puntos de código fuera del rango ASCII utilizarán las reglas de Unicode para la coincidencia de/i
, por lo que el modificador no realmente restringir las cosas a solo ASCII; simplemente prohíbe la mezcla de ASCII y no ASCII.Para resumir, este modificador proporciona protección para las aplicaciones que no desean estar expuestos a todo Unicode. Especificarlo dos veces da protección adicional.