2016-03-08 77 views
3

我試圖將我係統的宏下面的EBNF grammar編碼爲一個正則表達式(下圖),但儘管我盡了最大的努力,但看起來與多個宏匹配時很貪心:它不會在關閉}}@處停止。爲什麼這個正則表達式貪婪,當我告訴它不是?

Expand ``@{{...}}@`` references which may appear in Step parameters. 
    The syntax is described by the following EBNF grammar:: 

     depdata = "@{{", source identifier, ":", attribute, "}}@" 
       | "@{{TAGS:", expression, "}}@" ; 
     source identifier = ? printable 7-bit ASCII ? ; 
     attribute = "DATADIR" | "TAGSFILE" | "RESULT_INT" ; 
     expression = ? printable 7-bit ASCII ? ; 

和Python的正則表達式我已經拿出

@{{(?:(?:(?P<id>.*?):(?P<attr>DATADIR|TAGSFILE|RESULT_INT))|TAGS:(?P<expr>.+?))}}@ 

編輯:加內expr

Regular expression visualization

Debuggex Demo

失蹤 +

當在下面的測試情況發現所有的比賽,我希望得到的結果是3場比賽,但我只得到兩個:

@{{TAGS:sTagsJob << "job||ID||source"}}@ test @{{job:DATADIR}}@ email body @{{job:DATADIR}}@ blah 

我期待的比賽是:

  1. @{{TAGS:sTagsJob << "job||ID||source"}}@expr組集
  2. @{{job:DATADIR}}@idattr羣組設定
  3. @{{job:DATADIR}}@(再次)與idattr組設置

而是比賽是:

  1. @{{TAGS:sTagsJob << "job||ID||source"}}@ test @{{job:DATADIR}}@
  2. @{{job:DATADIR}}@

爲什麼非貪婪匹配(.+? )似乎表現貪婪?我錯過了什麼?我知道EBNF語法很傻,可以通過固定字符串出現在右邊來改善它,但這不是我的問題:我想了解爲什麼我的正則表達式具有失敗我)

+1

的'*',雖然「懶」,仍然會嘗試儘可能多的,因爲它可以返回一個有效的匹配相匹配。?。嘗試['@ {{(?:TAGS | [ - 〜] +?):(:(:DATADIR | TAGSFILE | RESULT_INT | [ - 〜] +?)}} @'](https://regex101.com/r/mJ9lX3/1) –

+0

如果您需要保留組:['@ {{(?P TAGS | [ - 〜] +?):(?:(?P DATADIR | TAGSFILE | RESULT_INT)|(?P [ - 〜] +?))}} @'](https://regex101.com/r/mJ9lX3/3)。 –

+0

我不得不在ASCII表上查找'[ - 〜]'來確定它是7位可打印的ASCII。太好了! – RobM

回答

3

點匹配任何字符(但換行符,如果DOTALL模式關閉)。**?匹配0+個字符,只是.*一次抓取所有內容,並且*?會一步步完成,在重試*?子版圖之前檢查後續子模式的匹配。你在這裏混淆了「貪婪」的含義:請注意,正則表達式試圖找到一個「無論如何」的匹配,當字符串中的某個位置發生不匹配時,它會重新量化子模式,引擎用貪婪的數量詞回溯,它確實是所有的東西都會變成取得一個匹配。惰性量詞並不能保證你的模式不會因量詞定義中額外的?而過火。

因此,避免使用點匹配模式,如果你不是真的這麼說,如果模式是已知的。在這裏,可打印的ASCII模式是必需的 - 然後使用它,不要依賴點匹配。

@{{(?P<id>TAGS|[ -~]+?):(?:(?P<attr>DATADIR|TAGSFILE|RESULT_INT)|(?P<expr>[ -~]+?))}}@ 

regex demo

注意[ -~]任何可打印的ASCII字符相匹配。見My favorite regex of all time

的模式匹配:

  • @{{ - 領先的分隔符
  • (?P<id>TAGS|[ -~]+?) - 一個id組匹配TAGS或1+可打印的ASCII字符,但儘可能少,因爲它也符合:(可以限制帶有向前看的字符類以排除:或用[ -9;-~]替換以使圖案更加優化並用該量詞消除?
  • : - 字面:
  • (?:(?P<attr>DATADIR|TAGSFILE|RESULT_INT)|(?P<expr>[ -~]+?)) - 匹配DATADIRTAGSFILE,或RESULT_INT和地方爲attr組,或一個或多個可打印的ASCII匹配(儘可能少)和地點到組「EXPR」。再一次,這裏很懶,因爲[ -~]匹配}。否則,您可以在這裏使用tempered greedy token(?:(?!}}@)[ -~])+。見demo
  • }}@ - 尾隨分隔符
0

這裏,似乎做你想要的正則表達式:

@{{(?:(?:(?P<id>[^:]*?):(?P<attr>DATADIR|TAGSFILE|RESULT_INT))|TAGS:(?P<expr>.*?))}}@ 

This correctly matches:

MATCH 1 
expr [8-37] `sTagsJob << "job||ID||source"` 

MATCH 2 
id [50-53] `job` 
attr [54-61] `DATADIR` 

MATCH 3 
id [79-82] `job` 
attr [83-90] `DATADIR` 

。在你的正則表達式,以防止一個錯誤它是否與匹配TAG部分(忘記了kleene中的P<expr>.?))}}@?)有關,但單憑這一點無法解決太多匹配問題,所以我在<id>之後將點改爲「非冒號」。

+1

'。*?'匹配除換行符以外的任何字符,但根據。到它應該匹配的語法描述*可打印的7位ASCII *。 '[^:] *?'也一樣(它只是匹配任何字符而不是':')。 –