あなたのアプローチには2つの問題があります。
- 最初の先読みは後読みである必要があります。を書く
(?!cat)
と、エンジンは次の 3 文字が正しいことを確認してcat
から、開始位置にリセットし (これが先の見方です)、同じ 3 文字で一致を試みます。dog
したがって、先読みは何も追加しません。dog
一致できる場合、明らかにcat
同じ位置で一致することはできません。あなたが望むのは(?<!cat)
、前の文字がそうでないことをチェックする後読みですcat
。残念ながら、JavaScript は後読みをサポートしていません。
- 2 つのルックアラウンドを論理ORする必要があります。あなたの場合、いずれかのルックアラウンドが失敗すると、パターンが失敗します。したがって、両方の要件 (どちらの端にもない)を満たす必要があります。しかし、実際にはそれをOR
cat
したいのです。後読みがサポートされている場合は、むしろ次のようになります(代替によりパターン全体が分割されることに注意してください)。しかし、私が言ったように、後読みはサポートされていません。最初のビットで 2 つのルックアラウンドを *OR* したように見える理由は、先行するものが単にチェックされていないためです (ポイント 1 を参照)。(?<!cat)dog|dog(?!cat)
catdogdog
cat
後読みを回避するにはどうすればよいですか?Kolink の答えは を示唆して(?!cat)...dog
おり、これは a が始まる位置にルックアラウンドを配置cat
し、先読みを使用します。これには 2 つの新しい問題があります:dog
文字列の先頭の a と一致できません (前の 3 文字が必要なためです。またdog
、一致が重複できないため、2 つの連続する s と一致することはできません (最初の と一致した後dog
、エンジンは 3 つの新しい文字を必要とします)。 、実際に再度一致する前に...
次を消費します)。dog
dog
場合によっては、パターンと文字列の両方を逆にすることで回避できるため、後読みを先読みに変えることができますが、あなたの場合は、最後の先読みを後読みに変えます。
正規表現のみのソリューション
私たちはもう少し賢くならなければなりません。一致は重複できないため、catdogcat
置換せずに (したがってターゲット文字列でそれらをスキップして) 明示的に一致を試み、見つかったすべてdog
の s を置換することができます。2 つのケースを交互に配置するため、文字列内のすべての位置で両方が試行されます (catdogcat
オプションが優先されますが、ここではあまり重要ではありません)。問題は、条件付き置換文字列を取得する方法です。しかし、これまでに得たものを見てみましょう。
text.replace(/(catdog)(?=cat)|dog/g, "$1[or 000 if $1 didn't match]")
したがって、最初の選択肢では、a を照合catdog
してグループに取り込み、1
別のcat
フォローがあることを確認します。置換文字列では、単純に$1
back を書きます。美しさは、2 番目の選択肢が一致した場合、最初のグループは使用されないため、代わりに空の文字列になることです。すぐcatdog
に一致するのではなく、一致するだけで先読みを使用する理由は、一致が重複しているためです。catdogcat
を使用した場合catdogcat
、入力catdogcatdogcat
で最初の一致が 2 番目までのすべてを消費するcat
ため、2 番目dog
は最初の選択肢によって認識されませんでした。
ここで唯一の問題は、2 番目の代替案を使用した場合、どのように000
代替品を入手するかということです。
残念ながら、入力文字列の一部ではない条件付き置換を呼び出すことはできません。秘訣は000
、入力文字列の末尾に a を追加し、 a が見つかった場合は先読みでそれをキャプチャし、dog
それを書き戻すことです。
text.replace(/$/, "000")
.replace(/(catdog)(?=cat)|dog(?=.*(000))/g, "$1$2")
.replace(/000$/, "")
000
最初の置換は文字列の末尾に追加されます。
2 番目の置換は、一致してcatdog
(別のcat
ものが続くことを確認)、それをグループにキャプチャする1
(2
空のままにする) か、一致してグループdog
にキャプチャ000
する2
(グループを空のままにする) かのいずれか1
です。それから$1$2
、飾り気のないものcatdog
か000
.
000
3 番目の置換は、文字列の末尾にある無関係なものを取り除きます。
コールバック ソリューション
正規表現の準備と 2 番目のオプションの先読みが苦手な場合は、代わりにコールバックを置き換えた少し単純な正規表現を使用できます。
text.replace(/(catdog)(?=cat)|dog/g, function(match, firstGroup) {
return firstGroup ? firstGroup : "000"
})
提供された関数のバージョンではreplace
、一致ごとに呼び出され、その戻り値が置換文字列として使用されます。関数の最初のパラメーターは試合全体、2 番目のパラメーターは最初のキャプチャ グループ (undefined
グループが試合に参加しない場合) などです。
したがって、置換コールバックでは、未定義の000
場合(つまり、オプションが一致した場合)を自由に呼び出したり、存在する場合 (つまり、オプションが一致した場合) を返すことができます。これはもう少し簡潔で、おそらく理解しやすいでしょう。ただし、関数を呼び出すオーバーヘッドにより、処理が大幅に遅くなります(ただし、これが問題になるかどうかは、これを実行する頻度によって異なります)。あなたのお気に入りを選んでください!firstGroup
dog
firstGroup
catdogcat