programing

"Variable length lookbehind가 구현되지 않았지만"가변 길이가 아닙니다.

nasanasas 2020. 12. 30. 08:15
반응형

"Variable length lookbehind가 구현되지 않았지만"가변 길이가 아닙니다.


진단하려는 매우 미친 정규식이 있습니다. 또한 매우 길지만 다음 스크립트로 줄였습니다. 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";
}

이로 인해 "정규식에서 구현되지 않은 가변 길이 lookbehind"오류가 발생합니다.

몇 가지 문제에 대해 도움을 주시면 감사하겠습니다.

  1. 가능한 모든 lookbehind 값이 "Monday", "Friday", "Sunday", "August"의 7 자이기 때문에이 오류가 발생하는 이유를 알 수 없습니다.
  2. 이 정규식을 직접 작성하지 않았으며 구문 (?i)(?-i). 내가 제거 (?i)하면 오류가 실제로 사라집니다. Perl은 정규식의이 부분을 어떻게 해석할까요? 괄호가 이스케이프되지 않고 닫는 괄호가 일치하지 않기 때문에 다른 구문 오류가 발생한다는 점을 제외하면 처음 두 문자는 "선택적 리터럴 괄호"로 평가됩니다.
  3. 이 동작은 Perl 5.16.3_64와 5.26.1_64 사이, 적어도 Strawberry Perl에서 시작됩니다. 전자 버전은 코드에 문제가없고 후자는 그렇지 않습니다. 왜 시작 되었나요?

나는 당신의 문제를 이것으로 줄였습니다.

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

인해의 존재 /i(케이스 둔감)과 같은 특정 문자 조합 개질제의 존재 "ss"또는 "st"하는 교체 할 수 Typographic_ligature 가 (가변 길이되게 /August/i모두 예를 들면 일치 AUGUST(6 개) → august(5 자, 마지막 U + FB06)).

그러나 /i(대소 문자를 구분하지 않는) 수정자를 제거하면 인쇄 합자가 일치하지 않기 때문에 작동합니다.

솔루션 :aa 수정자를 사용하십시오 .

/(?<!st)A/iaa

또는 정규식에서 :

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

에서 perlre :

ASCII / 비 ASCII 일치를 금지하려면 (예 : "\ N {KELVIN SIGN}"이있는 "k") "a"를 두 번 지정하십시오 (예 : /aai또는) /aia. ( "a"의 첫 번째 발생은 \d, 등을 제한하고 두 번째 발생은 "/ i"제한을 추가합니다.) 그러나 ASCII 범위 밖의 코드 포인트는 /i일치에 유니 코드 규칙을 사용 하므로 수정자는 그렇지 않습니다. 정말 ASCII로만 제한합니다. 그것은 단지 ASCII와 non-ASCII의 혼합을 금지합니다 .

여기에서 밀접하게 관련된 토론을 참조하십시오.


st합자가 될 수 있기 때문 입니다. 동일은 어떻게됩니까 fiff:

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

use utf8;

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

따라서 fi|fi실제로 대안의 길이가 동일하지 않은 경우를 상상해보십시오 .


st1 자 스타일 합자또는로 표시 될 수 있으므로 길이는 2 또는 1이 될 수 있습니다.

bash 명령을 사용하여 perl의 2 → 1 자 합자 전체 목록을 빠르게 찾습니다.

$ 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

이들은 각각 대표 , , , ß, 및 / 합자.
( 대표 ſt오래된 사용, 긴의 문자를 , 그것은 일치 st하고 않습니다 하지 일치 ft.)

펄은 또한 남아있는 문체 합자를 지원 하고 대한 ffiffllookbehinds 이미 문제를 갖고 있기 때문에 문제는이 맥락에서 주목할만한 것은 아니지만, / 별도.

Future releases of perl may include more stylistic ligatures, though all that remain are font-specific (e.g. Linux Libertine has stylistic ligatures for ct and ch) or debatably stylistic (such as the Dutch ij for ij or the obsolete Spanish for ll). It doesn't seem appropriate to have this treatment for ligatures that are not entirely interchangeable (nobody would accept dœs for does), though there are other scenarios, such as including ß thanks to its uppercase form being SS.

Perl 5.16.3 (and similarly old versions) only stumble on ss (for ß) and fail to expand the other ligatures in lookbehinds (they have fixed width and will not match). I didn't seek out the bugfix to itemize exactly which versions are affected.

Perl 5.14 introduced ligature support, so earlier versions don't have this problem.

Workarounds

Workarounds for /(?<!August)x/i (only the first will properly avoid August):

  • /(?<!Augus[t])(?<!Augu(?=st).)x/i (absolutely comprehensive)
  • /(?<!Augu(?aa:st))x/i (just the st in the lookbehind is "ASCII-safe" ²)
  • /(?<!(?aa)August)x/i (the whole the lookbehind is "ASCII-safe" ²)
  • /(?<!August)x/iaa (the whole regex is "ASCII-safe" ²)
  • /(?<!Augus[t])x/i (breaks ligature seeking ¹)
  • /(?<!Augus.)x/i (slightly different, matches more)
  • /(?<!Augu(?-i:st))x/i (case-sensitive st in lookbehind, won't match AugusTx)

These toy with removing the case-insensitive modifier¹ or adding the ASCII-safe modifier² in various places, often requiring the regex writer to specifically know of the variable-width ligature.

The first variation (which is the only comprehensive one) matches the variable widths with two lookbehinds: first for the six character version (no ligatures as noted in the first quote below) and second for any ligatures, employing a forward lookahead (which has zero width!) for st (including the ligatures) and then accounting for its single character width with a .

Two segments of the perlre man page:

¹ Case-insensitive modifier /i & ligatures

There are a number of Unicode characters that match a sequence of multiple characters under /i. For example, "LATIN SMALL LIGATURE FI" should match the sequence fi. Perl is not currently able to do this when the multiple characters are in the pattern and are split between groupings, or when one or more are quantified. Thus

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

² ASCII-safe modifier /aa (perl 5.14+)

To forbid ASCII/non-ASCII matches (like k with \N{KELVIN SIGN}), specify the a twice, for example /aai or /aia. (The first occurrence of a restricts the \d, etc., and the second occurrence adds the /i restrictions.) But, note that code points outside the ASCII range will use Unicode rules for /i matching, so the modifier doesn't really restrict things to just ASCII; it just forbids the intermixing of ASCII and non-ASCII.

To summarize, this modifier provides protection for applications that don't wish to be exposed to all of Unicode. Specifying it twice gives added protection.


Put (?i) after lookbehind:

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

or

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

To me it seems to be a bug.

참조 URL : https://stackoverflow.com/questions/50356241/variable-length-lookbehind-not-implemented-but-it-isnt-variable-length

반응형