2011-05-13 80 views
3

我有以下字符串:遞歸更換匹配標籤的正則表達式

<?foo?> <?bar?> <?baz?> hello world <?/?> <?/?> <?/?>

我需要一個正則表達式將其轉換成

<?foo?> <?bar?> <?baz?> hello world <?/baz?> <?/bar?> <?/foo?>

下面的代碼適用於非 - 遞歸標籤:

$x=preg_replace_callback('/.*?<\?\/\?>/',function($x){ 
    return preg_replace('/(.*<\?([^\/][\w]+)\?>)(.*?)(<\?\/?\?>)/s', 
      '\1\3<?/\2?>',$x[0]); 
},$str); 
+1

我的眼睛......他們正在流血...... – 2011-05-13 01:31:57

+0

我已決定放棄結束標記並始終要求用戶命名結束標記。這會讓我更容易解析它們,速度也更快,因爲我不需要依靠手動進行解析。 – romaninsh 2011-05-20 09:42:25

回答

1

你不能用正則表達式來做到這一點。你需要編寫一個解析器!

因此,創建一個堆棧(從最後添加和刪除項目的數組,使用array_push()array_pop())。

遍歷標籤,推動棧上已知的開啓標籤。

當你到一個結束標籤時,彈出堆棧,它會告訴你需要關閉的標籤。

+0

爲什麼不能使用(?R)? – romaninsh 2011-05-13 01:45:13

+0

因爲您需要解析器來解析任意的嵌套層次結構。 http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags – 2011-05-13 05:13:30

0

對於遞歸結構,請做一個遞歸函數。在某種形式的僞代碼中:

tags = ['<?foo?>', '<?bar?>', '<?baz?>'] 

// output consumed stream to 'output' and return the rest 
function close_matching(line, output) { 
    for (tag in tags) { 
    if line.startswith(tag) { 
     output.append(tag) 
     line = close_matching(line.substring(tag.length()), output) 
     i = line.indexof('<') 
     ... // check i for not found 
     output.append(line.substring(0, i)) 
     j = line.indexof('>') 
     ... // check j for error, and check what's between i,j is valid for close tag 
     output.append(closetag_for_tag(tag)) 
     line = line.substring(j + 1) 
    } 
    } 
    return line; 
} 

這應該會給你一個基本的結構。

+0

這應該匹配任何標籤,預先定義所有可能的標籤不是選項。此外,這也會失敗,並且包含<<(不包括<<?')的文本 – romaninsh 2011-05-13 01:57:19

+0

遞歸使用調用堆棧。迭代使用隱式堆棧。 – 2011-05-13 05:03:15