Найдите соответствующую скобку с помощью Regex

Входные данные - это строка, представляющая список элементов.

Список определяется как открытый фигурный {, за которым следует 0 или более элементов, разделенных пробелом, за которым следует закрытый фигурный }.

Элемент - это либо литерал, либо список элементов.

Литерал - это последовательность непробельных символов. Если элемент содержит фигурную скобку, он должен быть экранирован обратной косой чертой: \{ и \}. (Или вы можете предположить, что фигурные скобки не допускаются внутри литералов для простоты)

Пример:

"{abc { def ghi } 7 { 1 {2} {3 4} } {5 6} x\{yz \}foo }"

Никаких фигур внутри литералов:

"{abc { def ghi } 7 { 1 {2} {3 4} } {5 6} xyz foo }"

(Это упрощенное определение списка Tcl.)

Я хочу знать: можно ли разделить ввод на элементы внешнего цикла с помощью регулярного выражения?

Ожидаемый результат:

abc
{ def ghi }
7
{ 1 {2} {3 4} }
{5 6}
x{yz
}foo

Настоящий вопрос: можно ли это сделать с помощью Regex?

Меня больше всего интересует вариант .NET, но я приму любые ответы.

Я отправлю свое собственное предположение в ответ и посмотрю, подтверждено ли оно или уничтожено.


person Cristian Diaconescu    schedule 29.08.2010    source источник
comment
Почему }foo литерал, а 4} нет? Фактически, } - допустимый литерал по вашему определению.   -  person Kobi    schedule 29.08.2010
comment
@ Коби, ты прав. Я пытался получить определение, аналогичное тому, что делает интерпретатор Tcl, но он делает странные вещи. Например, это разрешит set a 3{4, но не set a {1 2 3{4 }. Аналогичное поведение для закрытых завитушек. Я обновлю вопрос.   -  person Cristian Diaconescu    schedule 30.08.2010


Ответы (4)


Что ж, при редактировании удаляются фигурные скобки из токенов и устраняется смысл вопроса, и теперь это легко сделать с помощью регулярных выражений .Net, используя группы балансировки. Это просто сопоставление фигурных скобок, что является базовым примером.
Как и ответ Кенни TM, это будет работать, только если вы удалите фигурные скобки верхнего уровня, или они будут соответствовать всему вводу.
Опять же, это лучше использовать для в рекреационных целях:

(?:                    # try matching...
    (?:\\[{}]|[^\s{}])+\s*? # a literal (allow escaped curly braces)
    |                       # OR
    (?<Curly>{)\s*          # "{" and push to stack
    |                       # OR
    (?<-Curly>})\s*?        # "}", pop from stack and fail if the stack is empty
)+?                    # ...a few times, and stop whenever you can.
(?(Curly)(?!))         # Make sure there aren't any extra open curly braces

Для получения более подробной информации см. Эту статью: Regex Balancing Group подробно

person Kobi    schedule 30.08.2010
comment
До того, как вопрос был обновлен, я не мог заставить это работать. По другому вопросу мы проверяем от начала до конца (^(?:...)+$), поэтому движок должен пробовать каждую комбинацию. Однако, когда вы сопоставляете токены, движок может удовлетворить меньшими затратами, и установить приоритеты будет сложно. - person Kobi; 30.08.2010

К сожалению, для некоторого вкуса Regex ответ ДА, например. PCRE и .NET, потому что они поддерживают рекурсивный шаблон и операции, подобные стеку соответственно.

Грамматику можно записать как

ELEMENT  -> (?!\{)\S+ | LIST
LIST     -> '\{\s*' ELEMENT? ('\s+' ELEMENT)* '\s*\}' 

таким образом, в PCRE это можно преобразовать в шаблон:

   \{\s*(?0)?(?:\s+(?0))*\s*\}|(?!\{)(?:[^\s}]|\}(?![\s}]))+

#  ---------------------------                   ^^^^^^^^^
#            LIST                    Make sure the } is not closing the group

См., Например, http://www.ideone.com/SnGsU (я удалил верхний уровень { и } для простоты).

(Конечно, на работе не пробуйте :))

(Кстати, я не знаю, как преобразовать этот PCRE в версию .NET. Если кто-то знает, попробуйте преобразовать шаблон рекурсивного регулярного выражения PCRE в .NET определение балансировочных групп)

person kennytm    schedule 29.08.2010
comment
Вау! Только один вопрос: что в вашем грамматическом определении означает (?! \ {) В начале ELEMENT? - person Cristian Diaconescu; 29.08.2010
comment
@Cristi: это негативный прогноз. - person kennytm; 29.08.2010
comment
Я хотел бы выбрать два ответа как «принятый ответ», так как этот довольно полный. Однако ответ Коби лучше соответствует тому, что я искал, а регулярное выражение там IMO немного более читабельно. - person Cristian Diaconescu; 01.09.2010

Традиционный ответ на этот вопрос - твердое «НЕТ». Как мы узнали в классе компиляторов, обычная грамматика не может использоваться для описания языка с рекурсивным определением (т.е. вы не можете использовать конечный автомат)

Здесь нужен контекстно-свободный синтаксический анализатор, реализация которого сводится к конечному автомату + СТЕК.
См. ANTLR, bison и т. Д.

person Cristian Diaconescu    schedule 29.08.2010
comment
В следующий раз вы можете подумать о том, чтобы оставить несколько минут, прежде чем опубликовать свой ответ, поскольку другим людям сложно получить какие-либо голоса, если уже есть сообщение, за которое проголосовали, поэтому это может оттолкнуть многих других людей от публикации ... или даже от поиска на вопрос (если на вопрос уже дан ответ, многие его даже не увидят). Я предполагаю, что вы заинтересованы в получении других мнений, иначе вы бы не писали ... не так ли? PS: в .NET я считаю, что это можно сделать с помощью регулярных выражений, но вы правы, что не рекомендуется использовать регулярное выражение для этой цели. - person Mark Byers; 29.08.2010
comment
@mark Примечание принято. И да, мне очень интересны ответы. Я помню, что где-то читал о некоторых менее ортодоксальных расширениях библиотеки регулярных выражений, которые позволяют сопоставление пар при определенных обстоятельствах, но я не могу вспомнить, какая библиотека или какие обстоятельства ... - person Cristian Diaconescu; 29.08.2010
comment
Я не могу следить за этим. Люди должны подождать, прежде чем публиковать правильные ответы? Можно ли использовать регулярные выражения для языков, требующих DPDA? - person user207421; 29.08.2010
comment
@EJP: Это самоответчик и, строго говоря, это неверно, потому что регулярное выражение в .NET на самом деле вообще не является регулярным и на самом деле оно может анализировать приведенное выше. Но в целом этот ответ по-прежнему является хорошим советом. Нет ничего плохого в том, чтобы публиковать собственные ответы, и нет ничего плохого в этом ответе, я просто говорю, что если он на самом деле хочет другого мнения, а не просто быстрого повышения репутации (а я предполагаю, что он это делает), тогда он должен иметь ждал перед отправкой, чтобы не отговаривать других людей отвечать. - person Mark Byers; 29.08.2010
comment
@mark Я перечитал ваш комментарий. Вы говорите, что это МОЖНО сделать в .NET? Укажите мне доказательства, и вы получите принятый ответ. - person Cristian Diaconescu; 29.08.2010
comment
@EJP Не знал аббревиатуры DPDA, поэтому поискал ее. Первый матч в Гугле хаймарк, в смысле туалетного юмора :) - person Cristian Diaconescu; 29.08.2010
comment
@Mark, не только .NET, но и почти все реализации регулярных выражений языков программирования, используемых в настоящее время, нельзя назвать обычными. Если реализация регулярного выражения поддерживает группы и обратные ссылки на эти группы или просматривает утверждения, это не является обычным явлением в более строгом определении. - person Bart Kiers; 29.08.2010
comment
@Crista Diaconescu: Лучшая информация, которую я смог найти по этому поводу, была здесь: stackoverflow.com/questions/3349999/ - person Mark Byers; 29.08.2010

@Cristi прав насчет регулярного выражения: теоретически невозможно решать рекурсивные выражения с помощью бесстекового конечного автомата. Решение, однако, проще: вам нужно только вести счетчик количества открытых скобок и следить за тем, чтобы оно не опускалось ниже нуля. Это больше экономит память, чем поддержка стека, и вам нужен только счетчик. - не содержание - круглых скобок.

Алгоритм:

counter = 0                        // Number of open parens
For char c in string:              
    print c                        
    if c=='{':                     // Keep track on number of open parens
        counter++
    if c=='}':
        counter--
    if counter==1:                 // New line if we're back to the top level
        print "\n"
    elif counter<1:                // Error if the nesting is malformed
        print "ERROR: parentheses mismatch"
        break
person Adam Matan    schedule 29.08.2010
comment
Верно, но исправить это довольно просто. - person Adam Matan; 02.09.2010