Захват внутренних элементов с использованием сбалансированного соответствия .net Regex - PullRequest
4 голосов
/ 01 сентября 2009

Я нашел следующие ресурсы по сбалансированному сопоставлению для регулярных выражений .net:

Из того, что я прочитал в них, должен работать следующий пример:

Это регулярное выражение должно найти «а» в любом месте группы угловых скобок, независимо от того, насколько глубоко. Он должен соответствовать "<a>", "<<a>>", "<a<>>", "<<>a>", "<<><a>>" и т. Д.

(?<=
    ^
    (
        (
            <(?<Depth>)
            |
            >(?<-Depth>)
        )
        [^<>]*?
    )+?
)
(?(Depth)a|(?!))

совпадение с "a" в строке "<<> a>"

Хотя он будет работать для строк "<a<>>" и "<<a>>", я не могу заставить его совпадать с "a", следующим за ">".

В соответствии с прочитанными мною объяснениями, первые два "<" должны увеличивать глубину вдвое, затем первое ">" должно уменьшать его один раз. На этом этапе (? (Глубина) a | (?!)) должна выполнить опцию «да», но регулярное выражение здесь даже не делает.

Рассмотрим следующее регулярное выражение, которое не выполняет такую ​​проверку и все еще не соответствует указанной строке:

(?<=
    ^
    (
        (
            <(?<Depth>)
            |
            >(?<-Depth>)
        )
        [^<>]*?
    )+?
)
a

Я что-то упустил или двигатель regex работает неправильно?

Ответы [ 4 ]

5 голосов
/ 02 сентября 2009

Если вы хотите найти каждое 'a', находящееся внутри сбалансированной пары угловых скобок, я бы предложил этот подход:

Regex r = new Regex(@"
    <
      (?>
         [^<>a]+
       |
         (a)
       |
         <(?<N>)
       |
         >(?<-N>)
      )+
    (?(N)(?!))
    >
", RegexOptions.IgnorePatternWhitespace);
string target = @"012a<56a8<0a2<4a6a>>012a<56789a>23456a";
foreach (Match m in r.Matches(target))
{
  Console.WriteLine("{0}, {1}", m.Index, m.Value);
  foreach (Capture c in m.Groups[1].Captures)
  {
    Console.WriteLine("{0}, {1}", c.Index, c.Value);
  }
}

результат:

9, <0a2<4a6a>>
11, a
15, a
17, a
24, <56789a>
30, a

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

4 голосов
/ 02 сентября 2009

Вы должны помнить, что просмотр назад просматривает только то, что ему нужно, чтобы удовлетворить встроенное регулярное выражение. Выражение в вашем lookbehind требуется только для соответствия угловой скобке, поэтому оно выглядит только так, как последняя. Если это левая угловая скобка, (?<Depth>) помещает пустую строку в стек, представленный этой группой захвата. Но если это правильная угловая скобка ...

Стоит отметить, что если при попытке выскочить (<-N>) не существует именованной группы N, то произойдет сбой ... *

Другими словами, это не условное выражение - (?(Depth)a|(?!)) - которое делает ваше регулярное выражение неудачным (как вы заметили), это попытка "уменьшить" "счетчик". Насколько я могу судить, ваше регулярное выражение в точности эквивалентно

(?<=<[^<>]*)a

Итак, чтобы ответить на ваш вопрос, соответствие сбалансированной конструкции .NET не нарушено. Византийская да, но сломанная, нет. : D

1 голос
/ 02 сентября 2009

Полностью пересмотренный ответ (первые два комментария были за предыдущий, неполный ответ):

Я понял, как этого добиться, чтобы я мог «заменить все» на результатах.

string input = @"a<a<<a>>a<a>a>a<a>a";
Regex reg = new Regex(@"
    (?<=
        <
        [^<>]*
        (?(ReverseDepth)(?!))
        (?:
            (?:
                <(?<-ReverseDepth>)
                |
                >(?<ReverseDepth>)
            )
            [^<>]*
        )*
    )
    a
    ", RegexOptions.IgnorePatternWhitespace);
Console.WriteLine(reg.Replace(input, "b"));

Это дает следующий вывод:

a<b<<b>>b<b>b>a<b>a

Теперь я понимаю, что в моем вопросе это не указано, но я никогда не задумывался о том, чтобы проверить, закрывается ли группа полностью или нет, поскольку текст, к которому я собираюсь применить это, предварительно проверен на xml. Однако, чтобы сопоставить мой ответ на вопрос и предотвратить сопоставление «а» в "<a", вместо приведенного мною здесь можно использовать следующее регулярное выражение:

Regex reg = new Regex(@"
    (?<=
        <
        [^<>]*
        (?(ReverseDepth)(?!))
        (?:
            (?:
                <(?<-ReverseDepth>)
                |
                >(?<ReverseDepth>)
            )
            [^<>]*
        )*
    )
    a
    (?=
        (?:
            (?:
                <(?<Depth>)
                |
                >(?<-Depth>)
            )
            [^<>]*
        )*
        (?(Depth)(?!))
        [^<>]*
        >
    )
    ", RegexOptions.IgnorePatternWhitespace);
1 голос
/ 01 сентября 2009

Обычно считается, что классы в библиотеке, используемой миллионами, не содержат серьезных ошибок: D

приведенное ниже регулярное выражение будет соответствовать всем вышеупомянутой версии <> a

var pattern =  "(" +
                       "((?'Open'<)[a]?)+" +
                       "((?'Close-Open'>)[a]?)+" +
                     ")*" +
                     "(?(Open)(?!))$";
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...