簡體   English   中英

PHP RegEx匹配嵌套模式(可能的遞歸)

[英]PHP RegEx to match nested patterns (possible recursion)

我正在嘗試匹配可能嵌套的模式。
這是一些示例數據,我想在這些數據中提取{{ loop ... }元素內的內容:

<ul>
    {{ loop #users as #u }}
        <li>{{ #u.first_name }} {{ #u.last_name }}</li>
    {{ endloop }}
</ul>

我用此RegEx正確得到它:

/{{\s+loop\s+#([a-zA-Z_][a-zA-Z0-9_]*)((?:\.[a-zA-Z0-9_]+)*)\s+as\s+#([a-zA-Z_][a-zA-Z0-9_]*)\s+}}(.*){{\s+endloop\s+}}/sU

說明:

  • /
  • {{元素的開始
    • \\s+loop\\s+ loop關鍵字
    • #([a-zA-Z_][a-zA-Z0-9_]*)變量名( 例如: #var
    • ((?:\\.[a-zA-Z0-9_]+)*)可選變量鍵( 例如:#var .key
    • \\s+as\\s+ 作為關鍵字
    • #([a-zA-Z_][a-zA-Z0-9_]*)\\s+別名變量名稱( 例如: #alias
  • }}元素結束
  • (.*)循環內容
  • {{\\s+endloop\\s+}} 閉環元素
  • /sU

失敗的地方

使用嵌套循環,我需要獲取第一級循環的內容(因為然后在我的項目中遞歸地解析內容)。 以下是一些示例數據:

 1| <ul>
 2|     {{ loop #users as #u }}
 3|         <li>
 4|             {{ #u.first_name }} {{ #u.last_name }}
 5|             <ul>
 6|                 {{ loop #u.friends as #f }}
 7|                     <li>{{ #f.first_name }} {{ #f.last_name }}</li>
 8|                 {{ endloop }}
 9|             </ul>
10|         </li>
11|     {{ endloop }}
12| </ul>
13| 
14| {{ loop #foo as #bar }}
15|     <a href="#">{{ #bar }}</a>
16| {{ endloop }}

使用此內容,模式將在遇到的第一個{{ endloop }}處停止(第2-8行)。
如果刪除U標志(不舒適),則無法使用多個循環,因為即使它們是不同的循環(第2-16行),它也會停止到最后一個{{ endloop }}
我有使用/m標志(多行)的模式的先前版本,但是它也失敗了,因為它只匹配最深層次的循環(第6-8行)。

我進行了很多嘗試(大多數是在regexr.com完成的 ),但是看不到任何進展。 我搜索了有關“遞歸模式”的解決方案,發現的最佳結果是這個問題,但是經過多次嘗試,我無法使其適應我的項目。


  • 是否有一個標志/標志組合為這種模式提供優先權?
  • 我讀了一些有關(?R) RegEx中的遞歸的信息,但是還沒有成功使用它,這對我有幫助嗎?
  • 顯而易見的最后一個問題:如何匹配第一級循環的全部內容?

我不僅在尋找解決方案,而且非常感謝了解如何解決這個問題。 鏈接到當前的RegexR: regexr.com/426fd

這是針對您的問題的性能解決方案(它需要幾百個步驟,而不是邪惡的上千個回溯步驟):

{{\s+loop\s+#(\w+)[^#]*#(\w+)\s*}}(?:[^{]*+|(?R)|{+)*{{\s+endloop\s+}}

在這里觀看現場演示

RegExp細目:

  • {{\\s+loop\\s+#(\\w+)[^#]*#(\\w+)\\s*}}匹配起始循環結構並捕獲散列詞
  • (?:非捕獲組的開始
    • [^{]*+匹配{
    • | 要么
    • (?R)重復整個模式
    • | 要么
    • {+匹配任意數量的大括號
  • )*盡可能匹配
  • {{\\s+endloop\\s+}}匹配結尾結構

這是您當前模式的快速修復:

{{\s+loop\s+#([a-zA-Z_]\w*)((?:\.\w+)*)\s+as\s+#([a-zA-Z_]\w*)\s*}}((?:(?!{{\s+(?:end)?loop\s).|(?R))*){{\s+endloop\s+}}

請注意,您不需要U修飾符即可使此模式按預期運行,但是您仍然需要s修飾符. 匹配任何字符。

正則表達式演示

主要區別在於將.*替換為(?:(?!{{\\s+(?:end)?loop\\s).|(?R))* 它與0個或多個重復匹配:

  • (?!{{\\s+(?:end)?loop\\s). -任何沒有開始滿足以下模式的序列的char( . ):
    • {{ - {{子字符串
    • \\s+ -1+空格
    • (?:end)? -可選的end子字符串
    • loop loop子字符串
    • \\s空格
  • | - 要么
  • (?R) -整個正則表達式模式

此外,如果您不使用u修飾語或(*UCP) PCRE動詞,則[a-zA-Z0-9_]等於\\w ,因此可以稍微縮短整個模式。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM