web-dev-qa-db-ja.com

引用符内にないすべてのインスタンスに一致する正規表現

this q/a から、引用符内の特定の正規表現notのすべてのインスタンスを一致させることは不可能であると推測しました。つまり、エスケープされた引用符とは一致しません(例:"this whole \"match\" should be taken")。私が知らないことをする方法があれば、それは私の問題を解決するでしょう。

ただし、そうでない場合は、JavaScriptで使用できる効率的な代替手段があるかどうかを知りたいと思います。私は少し考えましたが、すべてではないにしても、ほとんどの場合に機能するエレガントなソリューションはありません。

具体的には、.split()メソッドと.replace()メソッドを操作するための代替手段が必要なだけですが、より一般化できる場合は、それが最適です。

例:
の入力文字列:
+bar+baz"not+or\"+or+\"this+"foo+bar+
+を引用符ではなく#に置き換えると、次のように返されます。
#bar#baz"not+or\"+or+\"this+"foo#bar#

54
Azmisov

実際、文字列の引用符の内側にない正規表現のすべてのインスタンスに一致させることができます。この場合、各開始引用符は再び閉じられます。上記の例のように、\+と一致させたいとします。

ここで重要なのは、それに続く偶数個の引用符がある場合、Wordは引用符の外側にあるということです。これは、先読みアサーションとしてモデル化できます。

\+(?=([^"]*"[^"]*")*[^"]*$)

ここで、エスケープされた引用符をカウントしないようにします。これはもう少し複雑になります。次の引用に進む[^"]*の代わりに、バックスラッシュも考慮して[^"\\]*を使用する必要があります。バックスラッシュまたは引用符に到達した後、バックスラッシュに遭遇した場合は次の文字を無視するか、エスケープされていない次の引用符に進む必要があります。 (\\.|"([^"\\]*\\.)*[^"\\]*")のように見えます。合わせて、あなたはに到着します

\+(?=([^"\\]*(\\.|"([^"\\]*\\.)*[^"\\]*"))*[^"]*$)

little不可解であると認めます。 =)

91
Jens

Azmisov、あなたは_any efficient alternative that could be used in JavaScript_と_any elegant solutions that would work in most, if not all, cases_を探していると言ったので、この質問を復活させました。

言及されていない単純で一般的なソリューションがあります。

代替と比較して、このソリューションの正規表現は驚くほど簡単です。

_"[^"]+"|(\+)
_

アイデアは、一致するものの、引用内のすべてのものを無視して、そのコンテンツを中立化することです(代替の左側)。右側では、グループ1に無力化されなかったすべての_+_をキャプチャし、replace関数はグループ1を調べます。完全な動作コードは次のとおりです。

_<script>
var subject = '+bar+baz"not+these+"foo+bar+';
var regex = /"[^"]+"|(\+)/g;
replaced = subject.replace(regex, function(m, group1) {
    if (!group1) return m;
    else return "#";
});
document.write(replaced);
_

オンラインデモ

同じ原則を使用して、一致または分割できます。リファレンスの質問と記事を参照してください。コードサンプルも参照できます。

これにより、これを行う非常に一般的な方法の別のアイデアが得られることを願っています。 :)

空の文字列はどうですか?

上記は、テクニックを紹介する一般的な回答です。正確なニーズに応じて調整できます。テキストに空の文字列が含まれている可能性がある場合は、文字列キャプチャ式内の数量詞を_+_から_*_に変更するだけです。

_"[^"]*"|(\+)
_

デモ を参照してください。

エスケープされた引用はどうですか?

繰り返しますが、上記はテクニックを紹介する一般的な答えです。 「ignore this match」正規表現をニーズに合わせて調整できるだけでなく、複数の式を追加して無視できます。たとえば、エスケープされた引用符が適切に無視されることを確認したい場合、エスケープされた二重引用符に一致(および無視)するために、他の2つの前に_\\"|_を交互に追加することから始めます。

次に、二重引用符で囲まれた文字列の内容をキャプチャするセクション_"[^"]*"_内で、_"_が閉じセンチネルに変わる前にエスケープされた二重引用符が一致することを確認するために、代替を追加できます。 "(?:\\"|[^"])*"

結果の式には3つのブランチがあります。

  1. 一致する_\\"_およびignore
  2. "(?:\\"|[^"])*"が一致し、ignore
  3. 一致する_(\+)_、キャプチャおよびハンドル

他の正規表現フレーバーでは、この作業を後読みで簡単に行うことができますが、JSはサポートしていません。

完全な正規表現は次のようになります。

_\\"|"(?:\\"|[^"])*"|(\+)
_

regex demo および full script を参照してください。

参照

  1. 状況s1、s2、s3を除くパターンの一致方法
  2. ...以外のパターンの一致方法
50
zx81

3つの手順で実行できます。

  1. 正規表現のグローバル置換を使用して、すべての文字列本文の内容をサイドテーブルに抽出します。
  2. コンマ翻訳を行う
  3. 正規表現グローバル置換を使用して、文字列本体を元に戻します

以下のコード

// Step 1
var sideTable = [];
myString = myString.replace(
    /"(?:[^"\\]|\\.)*"/g,
    function (_) {
      var index = sideTable.length;
      sideTable[index] = _;
      return '"' + index + '"';
    });
// Step 2, replace commas with newlines
myString = myString.replace(/,/g, "\n");
// Step 3, swap the string bodies back
myString = myString.replace(/"(\d+)"/g,
    function (_, index) {
      return sideTable[index];
    });

設定後に実行する場合

myString = '{:a "ab,cd, efg", :b "ab,def, egf,", :c "Conjecture"}';

あなたは得る必要があります

{:a "ab,cd, efg"
 :b "ab,def, egf,"
 :c "Conjecture"}

ステップ1の後、動作します。

myString = '{:a "0", :b "1", :c "2"}'
sideTable = ["ab,cd, efg", "ab,def, egf,", "Conjecture"];

そのため、myString内のコンマのみが文字列外です。ステップ2、次にコンマを改行に変換します。

myString = '{:a "0"\n :b "1"\n :c "2"}'

最後に、数字のみを含む文字列を元のコンテンツに置き換えます。

6
Mike Samuel

Zx81による回答は最高のパフォーマンスとクリーンな回答のようですが、エスケープされた引用符を正しくキャッチするにはこれらの修正が必要です。

var subject = '+bar+baz"not+or\\"+or+\\"this+"foo+bar+';

そして

var regex = /"(?:[^"\\]|\\.)*"|(\+)/g;

また、すでに述べた「group1 === undefined」または「!group1」。特に2.は、元の質問で尋ねられたすべてを実際に考慮することが重要だと思われます。

ただし、このメソッドでは、エスケープされていない引用符のペアの外側で引用符がエスケープされていないことを暗黙的に必要とすることに注意してください。

1
Marius