1

私がやろうとしていること:特定のエスケープされていない文字を囲む最も内側のエスケープされていない角括弧を削除します(エスケープ\です)

input:[\[x\]]\]\[[\[y\]]
yの周りの角かっこを[\[x\]]\]\[\[y\]
探すときの出力:xの周りの角かっこを探すときの出力:\[x\]\]\[[\[y\]]

つまり、特定の文字の周りのエスケープされていない括弧のセットのみを削除します。

私はこれを(yに対して)試しましたが、それは最初のエスケープされていない(xの前)と最後の。Regex.Replace(input, @"(?<!\\)\[(.*?(?<!\\)y.*?)(?<!\\)\]",@"$1"と一致するようです。ワイルドカードを否定文字クラスに置き換えて除外および除外できると考えましたが、実際に否定する必要があるのは、これらのエスケープされていないバージョンであり、否定文字クラスのように否定の後ろ姿を取り入れようとすると、何も一致しないようです。まったく。[].[](?<!\\)

時間と労力をよろしくお願いします。

編集:

明確にするために、エスケープされていない角括弧の内容は、関心のあるエスケープされていない文字()が含まれている限り、何でもかまいません(別のエスケープされていない角括弧を除くy)。角かっこの内容はすべてそのままにしておく必要があります。

4

3 に答える 3

2

後読みは、この仕事には不適切なツールです。代わりにこれを試してください:

Regex r = new Regex(
  @"\[((?>(?:[^y\[\]\\]|\\.)*)y(?>(?:[^\[\]\\]|\\.)*))\]");

string s1 = @"[\[x\]]\]\[[\[y\]]";
Console.WriteLine(s1);
Console.WriteLine(r.Replace(s1, @"%$1%"));

Console.WriteLine();

string s2 = @"[\[x\]]\]\[[1234(\[abcycba\]\y\y)]";
Console.WriteLine(s2);
Console.WriteLine(r.Replace(s2, @"%$1%"));

結果:

[\[x\]]\]\[[\[y\]]
[\[x\]]\]\[%\[y\]%

[\[x\]]\]\[[1234(\[abcycba\]\y\y)]
[\[x\]]\]\[%1234(\[abcycba\]\y\y)%

(ブラケットを%削除するのではなく、 に置き換えて、何が置き換えられているかを正確に確認しやすくしました。)

(?:\\.|[^y\[\]\\])*は、(1) バックスラッシュの後に任意の文字が続く、または (2) 'y'、角括弧、またはバックスラッシュ以外の 0 個以上に一致します。次の文字が「y」の場合、それは消費さ(?:\\.|[^\[\]\\])*れ、次のエスケープされていない括弧までの残りの文字と一致します。否定された文字クラスに両方の大かっこを (バックスラッシュと共に)含めると、エスケープされていない大かっこの最も内側のセットのみに一致することが保証されます。

また、アトミックグループを使用することも重要です(?>...)。これにより、役に立たないことがわかっているバックトラッキングが防止され、正規表現が一致を含まない文字列で使用されたときに深刻なパフォーマンスの問題を引き起こす可能性があります。

別のアプローチでは、先読みを使用して「y」の存在をアサートしてから、はるかに単純な方法を使用し(?>(?:\\.|[^\[\]\\])*)て括弧内の文字を消費します。問題は、文字列に対して 2 つのパスを作成していることであり、先読みが先を見すぎたり、十分に先を見ていないことを確認するのが難しい場合があります。すべての作業を 1 回のパスで行うことで、マッチング プロセスの各段階で現在の位置を追跡することがはるかに簡単になります。

于 2010-02-02T01:03:44.133 に答える
2

このための正規表現を書くことは、問題に対して非常に複雑になる可能性があります。この関数は少し長くなりますが、概念的には単純で、トリックを実行します。

    string FixString(char x, string original)
    {
        int i = 0;
        string s = original;
        while (i < s.Length)
        {
            if (s[i] == x)
            {
                bool found = false;
                for (int j = i + 1; (j < s.Length) && !found; j++)
                {
                    if ((s[j] == ']') &&
                        (s[j-1] != '\\'))
                    {
                        s = s.Remove(j, 1);
                        found = true;
                    }
                }
                if (i > 0)
                {
                    found = false;
                    for (int j = i - 1; (j >= 0) && !found; j--)
                    {
                        if ((s[j] == '[') &&
                            ( (j == 0) ||
                              (s[j - 1] != '\\') ))
                        {
                            s = s.Remove(j, 1);
                            i--;
                            found = true;
                        }
                    }
                }
            }
            i++;
        }

        return s;
    }
于 2010-02-01T22:09:29.510 に答える
1

質問が編集された後に編集されました

Regex.Replace(input, @"((?<!\\)\[(?=((\\\[)|[^[])*((?<!\\)y)))|((?<=[^\\]y((\\\]|[^]]))*)(?<!\\)\])","");

削除する括弧を一致させたい:

(?<!\\)\[ - Match is an unescaped left bracket
(?=((\\\[)|[^[])*((?<!\\)y)) - Match is followed by any number of (escaped left brackets or non-left brackets) followed by an unescaped y

| - OR

(?<=[^\\]y((\\\]|[^]]))*) - Match is preceded by unescaped y followed by any number of (escaped right brackets or non-right brackets)
(?<!\\)\] - Match is an unescaped right bracket
于 2010-02-01T21:48:43.837 に答える