регулярное выражение: отрицательный взгляд назад в классе символов отрицания? (.NET аромат) - PullRequest
1 голос
/ 02 февраля 2010

Что я пытаюсь сделать: убрать внутренние неэкранированные квадратные скобки вокруг определенного неэкранированного символа (\ - escape)

ввод: [\[x\]]\]\[[\[y\]]
вывод при поиске скобок вокруг y: [\[x\]]\]\[\[y\]
выводится при поиске скобок вокруг x: \[x\]\]\[[\[y\]]

Короче говоря, удаляйте только неэкранированный набор скобок вокруг определенного символа.

Я пробовал это (для y): Regex.Replace(input, @"(?<!\\)\[(.*?(?<!\\)y.*?)(?<!\\)\]",@"$1", но это, кажется, соответствует первому неэкранированному [ (перед x) с последним ]. Я подумал, что мог бы заменить подстановочные знаки . классом отрицательных символов, чтобы исключить [ и ], но что мне действительно нужно отрицать, это unescaped их версий, и когда я пытаюсь включить с отрицательным видом, похожим на (?<!\\) в классе отрицательных символов, мне кажется, что он ничем не соответствует.

Заранее спасибо за ваше время и усилия.

редактирование:

Для пояснения, содержимое квадратных скобок без экранирования может быть любым (кроме другой квадратной скобки без экранирования), при условии, что они содержат интересующий вас символ спасения (y). Все содержимое скобок должно остаться.

Ответы [ 3 ]

2 голосов
/ 02 февраля 2010

Написание регулярного выражения для этого может быть слишком сложным для проблемы. Хотя эта функция немного длинна, она концептуально проста и делает свое дело:

    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;
    }
2 голосов
/ 02 февраля 2010

Lookbehind - неподходящий инструмент для этой работы. Попробуйте вместо этого:

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», квадратной скобки или обратной косой черты. Если следующим символом является 'y', оно расходуется, и (?:\\.|[^\[\]\\])* соответствует любым оставшимся символам до следующей неэкранированной скобки. Включение обеих скобок в класс отрицанных символов (вместе с обратной косой чертой) обеспечивает соответствие только самого внутреннего набора скобок без экранирования.

Также важно, чтобы вы использовали атомные группы - т.е., (?>...); это предотвращает возврат, который, как мы знаем, бесполезен и может вызвать серьезные проблемы с производительностью, когда регулярное выражение используется для строк, которые не содержат совпадений.

В альтернативном подходе можно использовать предпросмотр, чтобы утверждать наличие символа y, а затем использовать гораздо более простой (?>(?:\\.|[^\[\]\\])*), чтобы использовать символы в скобках. Проблема в том, что вы сейчас делаете два прохода над строкой, и может быть непросто убедиться, что заглядывающая сторона не смотрит слишком далеко вперед или недостаточно далеко. Выполнение всей работы за один проход значительно упрощает отслеживание того, где вы находитесь на каждом этапе процесса сопоставления.

1 голос
/ 02 февраля 2010

Отредактировано после редактирования вопроса

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...