2016-05-18 8 views
0

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

'~#A#B#' 
#^^^-- Replacement string. 
#| +---- Pattern string. 
#+------ Regular expression indicator (no need to escape strings A and B), 
#  which is only allowed if strings A and B are surrounded with ##. 
#  Strings A and B may not contain #, but are allowed to have ~. 

'#A#B#' 
#^------ When regex indicator is missing, strings A and B will be escaped. 

'A#B' 
#  Simplified form of '#A#B#', i. e. without the enclosing ##. 
#  Still none of the strings A and B is allowed to contain # at any position, 
#  but can have ~, so leading ~ should be treated as part of string A. 

Я попытался следующий шаблон (опять же, без кавычек):

'^((~)?(#))?([^#]+)#([^#]+)\3$' 

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

Однако эта схема работает только как ожидается, с наиболее полными типами входных данных:.

'~#A#B#' 
'#A#B#' 

но не для

'A#B' 

I. е, когда ведущая часть отсутствует, \3 не соответствует. Но если \3 заменяется на .*, совпадение будет успешным, и можно видеть, что ${BASH_REMATCH[3]} - пустая строка. Это то, что я не понимаю, при условии, что неустановленные переменные рассматриваются как пустые строки в Bash. Как мне сопоставить обратную ссылку с дополнительным контентом?

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

'^(~?)#([^#]+)#([^#]+)#$|^([^#]+)#([^#]+)$' 

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

Важное примечание. Как упоминал в своем комментарии @anubhava, сопоставление backreference может быть недоступен в некоторых строках Bash (возможно, это вопрос вариантов сборки, а не номера версии или даже какой-либо внешней библиотеки). Этот вопрос, конечно, ориентирован на те среды Bash, которые поддерживают такую ​​функциональность.

+0

Попробуйте ''^(~? #?) ([^ #] +) # ([^ #] +) \ 1 $ '', или, возможно, если' ~ 'не нужно проверять на наличие как в начале и в конце строки попробуйте '^ ~? (#?) ([^ #] +) # ([^ #] +) \ 1 $' –

+0

Простите за то, что вы не так понятны, но ведущий ' 'может присутствовать только в том случае, если' # 'есть - они не являются двумя независимыми частями. –

+0

Попробуйте ['^ (~? (#?)) ([^ #] +) # ([^ #] +) \ 2 $'] (https://regex101.com/r/sF1qY1/1) –

ответ

3

Есть два способа решения этой проблемы:

  1. Вместо того, чтобы группа опциональной (другие слова, что позволяет ему не соответствовать вообще), сделать его обязательным, но совпадает с пустой строкой. Другими словами, меняются конструкции, такие как (#)? - (#?).

  2. Использовать условное соответствие для обратной ссылки \3, только если группа 3 соответствует. Для этого измените \3 на (?(3)#|).

Как правило, первый вариант предпочтительнее из-за его большей читаемости. Кроме того, регулярные выражения Баша, похоже, не поддерживают условные конструкции, поэтому нам нужно сделать работу с параметром 1. Это сложно из-за дополнительного условия, что ~ разрешено только в том случае, если также присутствует #.Если bash поддерживает взгляды, мы могли бы сделать что-то вроде ((~)(?:#))?(#?). Но так как это не так, нам нужно проявить творческий подход. Я придумал следующую схему:

^((~(#))|(#?))([^#]+)#([^#]+)(\3|\4)$ 

Demo.

Идея состоит в том, чтобы использовать оператор чередования | для обработки двух разных случаев: либо текст начинается с ~#, либо нет. ((~(#))|(#?)) захватывает ~# в группе 2 и # в 3-й группе, если это возможно, но если там нет ~ то просто захватывает # (если таковой имеется) в группе 4. Тогда мы можем использовать (\3|\4) в конце, чтобы соответствовать закрытия #, если была (помните, группа 3 снята #, если текст начат с ~#, а группа 4 снята # или пустая строка, если текст не начинаются с ~#).

+0

Хорошая точка в опции 1 ('^ ((~)? (#) |) ([^ #] +) # ([^ #] +) \ 3 $'), но, к сожалению, она имеет тот же эффект - когда альтернативный маршрут берется, '\ 3' больше не соответствует, хотя' $ {BASH_REMATCH [*]} 'выглядит так, как ожидалось. Однако условное совпадение, похоже, не работает - поддерживается ли оно в Bash? –

+0

Антон, '\ 3' относится только к' (#) '. Вам нужно использовать '\ 1' –

+0

@AntonSamsonov Он должен быть' (#?) ', А не' (#) | '. –