2016-05-18 19 views
0

在試圖使用擊的內置正則表達式匹配來解析以下類型的串,其是要被轉換成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$' 

也就是說,它聲明領先~#可選的(和~它更可選),然後捕獲部分AB,並且只有當它存在於領導者中時,尾要求#才存在。前導#僅用於反向引用匹配 - 其他地方不需要,而~被捕獲以供腳本後續檢查。

然而,這種模式只適用預期與種類最齊全的輸入數據:

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

而不是

'A#B' 

一,E,每當龍頭部分缺失, \3無法匹配。但是,如果將\3替換爲.*,則匹配成功,可以看出${BASH_REMATCH[3]}是空字符串。這是我不明白的地方,假設未設置的變量在Bash中被視爲空字符串。 然後,我如何將反向引用與可選內容進行匹配?

作爲一種變通方法,可以寫一個替代圖案

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

但它導致獨特捕獲基團對於每個可能的情況下,這使得代碼不太直觀。

重要說明。正如@anubhava在他的評論中提到的,反向引用匹配可能在某些Bash版本中不可用(可能是構建選項而不是版本號,甚至是某個外部庫的問題)。這個問題當然是針對那些支持這種功能的Bash環境。

+0

嘗試''^(〜?#?)([^#] +)#([^#] +)\ 1 $'',或者如果不需要檢查「〜」 (#^)([^#] +)#([^#] +)\ 1 $' –

+0

對不起,不太清楚,但領先的'〜〜 '只有'#'存在才能存在 - 它們不是兩個獨立的部分。 –

+0

嘗試[^ ^(〜?(#?))([^#] +)#([^#] +)\ 2 $'](https://regex101.com/r/sF1qY1/1) –

回答

3

有兩種方法來解決這個問題:

  1. 代替把組可選的(換句話說,允許它不匹配的話),使之強制性的,但匹配空字符串。換句話說,改變結構像(#)?(#?)

  2. 僅當組3匹配時,才使用條件來匹配反向引用\3。爲此,請將\3更改爲(?(3)#|)

通常,第一個選項是可取的,因爲它的可讀性更好。另外,bash的正則表達式似乎不支持條件結構,所以我們需要做出選項1的工作。這是困難的,因爲附加條件~只有在#也存在時才被允許。如果bash支持lookaheads,我們可以做類似((~)(?:#))?(#?)。但既然沒有,我們需要發揮創意。我想出了以下模式:

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

Demo

這個想法是利用交替運算符|來處理兩種不同的情況:文本以~#開頭,或者它不是。 ((~(#))|(#?))捕獲組2 ~##在組3如果可能的話,但如果沒有~那麼它只是捕獲#(如果存在)在組4。然後,我們可以使用(\3|\4)末以匹配閉合#,如果有一個打開一個(記住,如果文本以~#開始,並且組4捕獲#或者如果文本確實是而不是~#開始,則組3捕獲#)。

+0

選項1('^((〜)?(#)|)([^#] +)#([^#] +)\ 3 $')的好處,但不幸的是它具有相同的效果 - 替代路線被採用,'\ 3'不再匹配,雖然'$ {BASH_REMATCH [*]}'看起來像預期的那樣。然而,條件匹配似乎並不奏效 - Bash支持它嗎? –

+0

Антон,'\ 3'僅指'(#)'。你需要使用'\ 1' –

+0

@AntonSamsonov它應該是'(#?)',而不是'(#)|'。 –

相關問題