tester regular pattern one regex perl

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:

  1. 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".
  2. 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.
  3. 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 .

Vea una discusión estrechamente relacionada aquí


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 o , 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 , , , ß y / .
( 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, y para ffi y ffl , aunque esto no es significativo en este contexto, ya que las miradas ya tienen problemas con y / 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 el st 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á con AugusTx )

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 secuencia fi . 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 la a dos veces, por ejemplo /aai o /aia . (La primera aparición de a 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.