17

Perl 正規表現では\1\2、 などの式は通常、以前にキャプチャされたグループへの「後方参照」として解釈されますが\1\2、 などが文字クラス内に表示される場合はそうではありません。後者の場合、\はエスケープ文字として扱われます (したがって、\1単に1などになります)。

したがって、(たとえば) 最初の文字が最後の文字と一致するが、文字列の他の場所には現れない文字列 (長さが 1 より大きい) に一致させたい場合、次の正規表現は機能しません。

/\A       # match beginning of string;
 (.)      # match and capture first character (referred to subsequently by \1);
 [^\1]*   # (WRONG) match zero or more characters different from character in \1;
 \1       # match \1;
 \z       # match the end of the string;
/sx       # s: let . match newline; x: ignore whitespace, allow comments

(たとえば)文字列に一致するため、機能しませ'a1a2a'

  DB<1> ( 'a1a2a' =~ /\A(.)[^\1]*\1\z/ and print "fail!" ) or print "success!"
fail!

通常は回避策1を見つけることができますが、それは常に問題固有のものであり、文字クラス内で後方参照を使用できる場合よりもはるかに複雑に見えます。

一般的な(できれば簡単な) 回避策はありますか?


1たとえば、上記の例の問題については、次のようなものを使用します

/\A
 (.)              # match and capture first character (referred to subsequently
                  # by \1);
 (?!.*\1\.+\z)    # a negative lookahead assertion for "a suffix containing \1";
 .*               # substring not containing \1 (as guaranteed by the preceding
                  # negative lookahead assertion);
 \1\z             # match last character only if it is equal to the first one
/sx

...ここで、以前の正規表現のかなり単純な (残念ながら、正しくない) 部分式[^\1]*を、やや禁止されている否定的な先読み assertionに置き換えました(?!.*\1.+\z)\1このアサーションは基本的に「これ以降 (最後の位置以外) に現れたらあきらめる」というものです。ちなみに、この解決策は、質問で言及した回避策の種類を説明するためだけに提供しています。特に優れているとは言いません。

4

1 に答える 1

14

これは、繰り返されるグループ内で否定先読みを行うことで実現できます。

/\A         # match beginning of string;
 (.)        # match and capture first character (referred to subsequently by \1);
 ((?!\1).)* # match zero or more characters different from character in \1;
 \1         # match \1;
 \z         # match the end of the string;
/sx

このパターンは、グループに複数の文字が含まれている場合でも使用できます。

于 2013-08-14T20:57:24.650 に答える