Написание регулярного выражения для захвата текста между внешними скобками

Итак, я пытаюсь проанализировать файл с текстом в этом формате:

outerkey = (innerkey = innervalue)

Это становится более сложным. Это также допустимо в файле:

outerkey = (innerkey = (twodeepkey = twodeepvalue)(twodeepkey2 = twodeepvalue2))

Поэтому я хочу в основном захватить только текст внешнего ключа. Я не могу гарантировать, что весь текст будет в одной строке. Возможно, что значение будет в нескольких строках. И в файле более одного элемента.

Итак, вот мое регулярное выражение:

[^\s=]+\s*=\s*(\(\s*.*\s*\))

Цель для меня просто заменить первую часть [^\s=]+ с ключом, который я хочу найти, и я получаю весь текст внешней скобки.

Здесь проблема. Мое регулярное выражение будет не только захватывать текст, который я хочу захватить, но также захватывать текст из следующей группы, поскольку регулярные выражения являются жадными. Если вы не сделаете его жадным, то это тоже не сработает, так как при первой закрывающей скобке перестанет фиксироваться.

В конечном итоге, если у меня есть следующая строка

foo = 
(
  ifoo = ifoov
)

bar =
(
  ibar =
    (iibar = iibarv)
    (iibar2 = iibarv2)
)

Я хочу, чтобы группы совпадали

(
  ifoo = ifoov
)

а также

(
  ibar =
    (iibar = iibarv)
    (iibar2 = iibarv2)
)

Прямо сейчас это будет соответствовать

(
  ifoo = ifoov
)

bar =
(
  ibar =
    (iibar = iibarv)
    (iibar2 = iibarv2)
)

Кстати, я запускаю это в многострочном и однострочном режиме.

Есть идеи? Спасибо!

2 ответа

Решение

Вообще говоря, регулярное выражение не может считать совпадения, так что это нелегко сделать. .NET, однако, имеет функцию, называемую "определения балансировочной группы". В этом примере показано, как сопоставить парные угловые скобки, и вам это нужно...

Мне удалось настроить функцию регулярного выражения определения.NET для этой проблемы следующим образом:

Regex r = new Regex(@"(?x) # for sanity!

    (?'Key' [^=\s]* )
    \s*=\s*
    (?'Value'
      (
         (
           [^()]*
           (?'Open'\()
         )+
         (
           [^()]*
           (?'Close-Open'\))
         )+
      )+?
    )
    (?(Open)(?!))

");

Затем мы можем проверить это следующим образом:

var text = @"
foo = 
(
  ifoo = ifoov
)

bar =
(
  ibar =
    (iibar = iibarv)
    (iibar2 = iibarv2)
)

outerkey = (innerkey = (twodeepkey = twodeepvalue)(twodeepkey2 = twodeepvalue2))
";

foreach (Match m in r.Matches(text)) {
  Console.WriteLine("Key: [{0}]", m.Groups["Key"]);
  Console.WriteLine("Value: [{0}]", m.Groups["Value"]);
  Console.WriteLine("-------");
}
Console.WriteLine("That's all folks!");

Это печатает ( как видно на ideone.com):

Key: [foo]
Value: [(
  ifoo = ifoov
)]
-------
Key: [bar]
Value: [(
  ibar =
    (iibar = iibarv)
    (iibar2 = iibarv2)
)]
-------
Key: [outerkey]
Value: [(innerkey = (twodeepkey = twodeepvalue)(twodeepkey2 = twodeepvalue2))]
-------
That's all folks!

Некоторые незначительные изменения из примера шаблона из документации:

  • Открыто - закрыто - ни скобки сейчас \( - \) - [^()] вместо < - > - [^<>]
  • Сбалансированная структура повторяется с +? (хотя бы один, но как можно меньше) вместо *
  • "содержание" сопоставляется до, а не после скобок
Другие вопросы по тегам