56 votes

"L'arrière-plan à longueur variable n'est pas implémenté" mais ce n'est pas une longueur variable

J'ai un très fou regex que je suis en train de diagnostiquer. Il est également très long, mais j'ai coupé vers le bas, juste le script suivant. Exécuter à l'aide de 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";
}

Cela donne l'erreur "de longueur Variable lookbehind pas mis en œuvre dans la regex."

J'espère que vous pouvez aider de plusieurs questions:

  1. Je ne vois pas pourquoi cette erreur se produit, parce que tous les lookbehind valeurs sont de 7 caractères: "lundi ", "vendredi ", "dimanche ", "août ".
  2. Je n'ai pas écrit cette regex moi-même, et je ne suis pas sûr de savoir comment interpréter la syntaxe (?i) et (?-i). Quand je me débarrasser de l' (?i) l'erreur va en fait plus loin. Comment perl interpréter cette partie de la regex? Je pense que les deux premiers caractères sont évalués à "facultatif littérale entre parenthèses", sauf que les parenthèses n'est pas échappé, et aussi dans ce cas, je voudrais avoir une erreur de syntaxe car la fermeture de parenthèses serait alors de ne pas être égalé.
  3. Ce comportement commence quelque part entre Perl 5.16.3_64 et 5.26.1_64, au moins dans Strawberry Perl. L'ancienne version est fine avec le code, le second ne l'est pas. Pourquoi avait-il commencer?

76voto

anubhava Points 172509

J'ai réduit votre problème:

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");

En raison de la présence de l' /i (insensible à la casse) modificateur et la présence de certaines combinaisons de caractères tels que "ss" ou "st" qui peut être remplacée par une Typographic_ligature à l'origine pour être une longueur variable (/August/i correspond par exemple sur les deux AUGUST (6 caractères) et august (5 caractères, le dernier en U+FB06)).

Cependant, si nous enlevons /i (insensible à la casse) modificateur alors cela fonctionne parce que les ligatures typographiques ne sont pas appariés.

Solution: Utiliser aa modificateurs c'est à dire:

/(?<!st)A/iaa

Ou dans votre 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");

De perlre:

D'interdire ASCII/non-ASCII correspond à (comme le "k" avec des "\N{KELVIN SIGNE}"), spécifier la "une" à deux reprises, par exemple /aai ou /aia. (La première occurrence de "une" limite l' \d, etc., et la deuxième occurrence ajoute le "/i" restrictions.) Mais, notez que les points de code en dehors de la plage ASCII utilise Unicode règles pour /i correspondant, de sorte que le modificateur n'est pas vraiment restreindre tout simplement ASCII; il a juste interdit le brassage de l'ASCII et non-ASCII.

Voir un de près la discussion à ce sujet ici

21voto

choroba Points 56333

C'est parce que st peut être une ligature. La même chose se produit pour fi et ff :

 #!/usr/bin/perl
use warnings;
use strict;

use utf8;

my $fi = 'fi';
print $fi =~ /fi/i;
 

Imaginez donc quelque chose comme fi|fi où, en effet, les longueurs des alternatives ne sont pas les mêmes.

2voto

Adam Katz Points 857

st , pourrait être représenté par un 1-caractère stylistique ligature comme ou , de sorte que sa longueur peut être de 2 ou 1.

Trouver rapidement perl liste complète des 2→1-caractère ligatures à l'aide d'une commande 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

Ces représentent respectivement l' , , , ß, et / des ligatures.
( représente ſt, à l'aide de l'obsolètes longue s de caractères; il correspond st et il n'a pas de correspondance ft.)

Perl prend également en charge le reste stylistique ligatures, et pour ffi et ffl, même si ce n'est pas de noter dans ce contexte depuis lookbehinds ont déjà des problèmes avec et / séparément.

Les futures versions de perl peut inclure plus de stylistique ligatures, si tout ce qui reste sont des polices spécifiques (par exemple, Linux Libertine a stylistique des ligatures pour ct et ch) ou discutable de stylistique (tels que les hollandais ij pour ij ou obsolètes espagnol, pour ll). Il ne semble pas approprié de ce traitement pour les ligatures qui ne sont pas totalement interchangeables (personne n'accepterait dœs pour does), bien qu'il existe d'autres scénarios, comme l'inclusion d' ß grâce à ses majuscules être SS.

Perl 5.16.3 (et de même les anciennes versions) seulement tomber sur ss (pour ß) et ne parviennent pas à développer les autres ligatures lookbehinds (ils ont une largeur fixe et ne correspondent pas). Je n'ai pas chercher la correction de détailler exactement qui sont concernés.

Perl 5.14 introduit ligature de soutien, de sorte que les versions antérieures n'ont pas ce problème.

Solutions de contournement

Des solutions de contournement pour /(?<!August)x/i (seul le premier sera correctement éviter August):

  • /(?<!Augus[t])(?<!Augu(?=st).)x/i (absolument complet)
  • /(?<!Augu(?aa:st))x/i (juste l' st dans le lookbehind est "ASCII-safe" 2)
  • /(?<!(?aa)August)x/i (de l'ensemble de l'lookbehind est "ASCII-safe" 2)
  • /(?<!August)x/iaa (l'ensemble de la regex est "ASCII-safe" 2)
  • /(?<!Augus[t])x/i (pauses ligature recherche 1)
  • /(?<!Augus.)x/i (légèrement différentes, correspond plus)
  • /(?<!Augu(?-i:st))x/i (sensible à la casse st dans lookbehind, de ne pas correspondre AugusTx)

Ces jouets avec la suppression de la casse modificateur de1 ou de l'ajout de l' ASCII-safe modificateur2 dans divers endroits, nécessitant souvent la regex écrivain à savoir notamment de la largeur variable de la ligature.

La première variante (qui est la seule complète) correspond à la variable largeurs avec deux lookbehinds: d'abord pour les six caractères de la version (pas de ligatures comme indiqué dans la première citation ci-dessous) et la seconde pour toutes les ligatures, le recours à une avant d'anticipation (qui a une largeur égale à zéro!) pour st (y compris les ligatures), puis de rendre compte de son caractère unique de largeur avec un .

Deux segments de l' perlre page de man:

1 casse modificateur /i & ligatures

Il y a un certain nombre de caractères Unicode qui correspondent à une séquence de plusieurs personnages en vertu de l' /i. Par exemple, "minuscule LATINE LIGATURE FI" doit correspondre à la séquence de fi. Perl n'est pas actuellement en mesure de effectuez cette opération lorsque plusieurs personnages sont dans le modèle et sont partagé entre groupements, ou lorsqu'un ou plusieurs sont quantifiés. Ainsi

"\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!

2 ASCII-safe modificateur /aa (perl 5.14+)

D'interdire ASCII/non-ASCII correspond à (comme k avec \N{KELVIN SIGN}), spécifiez l' a deux fois, par exemple /aai ou /aia. (La première l'apparition de l' a limite l' \d, etc., et la deuxième occurrence ajoute l' /i restrictions.) Mais, notez que les points de code en dehors de la Plage ASCII utilise Unicode règles pour /i correspondant, de sorte que le modificateur n'est pas vraiment restreindre tout simplement ASCII; c'est tout simplement interdit l' le brassage de l'ASCII et non-ASCII.

Pour résumer, cette option offre une protection pour les applications qui ne souhaite pas être exposé à l'ensemble de l'Unicode. La spécification de deux fois donne une protection supplémentaire.

0voto

Hegel F. Points 135

Mettez (?i) après le lookbehind:

(?<!(Mon|Fri|Sun)day |August )(?i)abcd(?-i)

ou

(?<!(Mon|Fri|Sun)day |August )(?i:abcd)

Pour moi, cela semble être un bug.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X