編集
次の解決策には、2 つの重大な問題があります。
- 進行しすぎるため
XXX
、同じ行で始まる複数のシーケンスに一致することはできません。pos
X
2 番目の解決策は正しくありません。2 つの行が互いに上にある連続した行に一致します。必ずしも3つ連続する必要はありません。
したがって、すべての賛成票 (および報奨金) は、m.buettnerの包括的な .NET 回答またはQtax自身からの魅力的な PCRE 回答のいずれかに送られる必要があります。
元の回答
これは、正規表現への Perl コードの埋め込みを使用した回答です。Perl 正規表現は、コードを使用して正規表現内の任意の条件をアサートしたり、部分的な正規表現を発行したりできるため、通常の言語または文脈自由言語の一致に限定されず、チョムスキー階層の上位にある言語の一部と一致できます。
一致させたい言語は、正規表現で次のように記述できます。
^ .{n} X .*\n
.{n} X .*\n
.{n} X
はn
数字です。これは、文脈依存言語の標準的な例である a n b n c n 言語に一致させるのと同じくらい複雑です。
最初の行は簡単に照合でき、Perl コードを使用して他の行の正規表現を発行できます。
/^ (.*?) X
(?: .*\n (??{"." x length($1)}) X){2}
/mx
短かった!それは何をするためのものか?
この正規表現は、3 つが重なっているすべての文字列に一致するようになりX
ました。
そのようなシーケンスをすべて抽出したい場合は、気の利いたものにする必要があります。シーケンスが重複する可能性があるため、たとえば
.X
XX
XX
X.
次の試合が始まる位置は、最初の試合を過ぎてはなりませんX
。これは、後読みと先読みを介して行うことができます。Perl は一定長の後読みのみをサポートしますが、 \K
同様のセマンティクスを提供するエスケープを備えています。したがって
/^ (.*?) \K X
(?=( (?: .*\n (??{"."x length($1)}) X ){2} ))
/gmx
3 つの垂直X
es のすべてのシーケンスに一致します。試験時間:
$ perl -E'my$_=join"",<>; say "===\n$1X$2" while /^(.*?)\KX(?=((?:.*\n(??{"."x length($1)})X){2}))/gmx' <<'END'
....X.......
..X..X...X....
X.X...X..X.....
X....XXXXXX.....
X..XXX...........
.....X..........
..............X
..X...........X....
..X...........X....X...
....X.....
END
===
..X..X...X....
X.X...X..X.....
X....XXXXX
===
X.X...X..X.....
X....XXXXXX.....
X
===
X....XXXXXX.....
X..XXX...........
.....X
===
..............X
..X...........X....
..X...........X
注: これは、少なくとも Perl 5、v10 以降で利用可能な実験的な正規表現機能に依存しています。コードは v16 perl でテストされました。
コードを埋め込まないソリューション
行を見てみましょう
...X...\n
...X..\n
...
各行の先頭部分は同じ長さであると断言したい。base case を使用した再帰によってこれを行うことができますX.*\n
。
(X.*\n|.(?-1).)X
それを行頭に固定すると、2 つの垂直X
es を一致させることができます。2 行以上を一致させるには、先読みで再帰を実行してから、一致位置を次の行に進めて繰り返す必要があります。このために、単純に を一致させ.*\n
ます。
X
これにより、3 つの垂直esを持つ文字列に一致する次の正規表現が得られます。
/ ^
(?:
(?=( X.*\n | .(?-1). ) X)
.*\n # go to next line
){2}
/mx
しかし、このようなすべてのシーケンスに一致させたいので、これでは十分ではありません。これを行うために、基本的に正規表現全体を先読みに入れます。正規表現エンジンは、新しい一致を生成するたびに位置を確実に進めます。
/ ^
(?=
(
(?:
(?= (X.*\n | .(?-1). ) X)
.*\n # go to next line
){2}
.* # include next line in $1
)
)
/mx
試験時間:
$ perl -E'my$_=join"",<>; say "===\n$1" while /^(?=((?:(?=(X.*\n|.(?-1).)X).*\n){2}.*))/gmx' <<'END'
....X.......
..X..X...X....
X.X...X..X.....
X....XXXXXX.....
X..XXX...........
.....X..........
..............X
..X...........X....
..X...........X....X...
....X.....
END
===
..X..X...X....
X.X...X..X.....
X....XXXXXX.....
===
X.X...X..X.....
X....XXXXXX.....
X..XXX...........
===
X....XXXXXX.....
X..XXX...........
.....X..........
===
..............X
..X...........X....
..X...........X....X...
したがって、これは埋め込みコードを使用したソリューションと同様に機能します。つまり、X
es の各グループではなく、行の各グループを垂直 es に一致させX
ます。(実際、このソリューションは、埋め込みコードよりも壊れやすいようです)