2012-04-04 63 views
3

我試圖在每個分號後插入一個空格,除非分號是HTML實體的一部分。這裏的例子很簡短,但我的字符串可能很長,有幾個分號(或沒有)。在分號後插入空格,除非它是HTML實體的一部分

Coca‑Cola =>  Coca‑Cola (‑ is a non-breaking hyphen) 
Beverage;Food;Music => Beverage; Food; Music 

我發現下面的正則表達式確實爲短字符串的伎倆:

<?php 
$a[] = 'Coca&#8209;Cola'; 
$a[] = 'Beverage;Food;Music'; 
$regexp = '/(?:&#?\w+;|[^;])+/'; 
foreach ($a as $str) { 
    echo ltrim(preg_replace($regexp, ' $0', $str)).'<br>'; 
} 
?> 

然而,如果字符串是有點大,上面的preg_replace實際上崩潰我的Apache服務器(在連接)將以下代碼添加到上面的示例代碼中:

$a[] = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. '. 
    'In blandit metus arcu. Fusce eu orci nulla, in interdum risus. '. 
    'Maecenas ut velit turpis, eu pretium libero. Integer molestie '. 
    'faucibus magna sagittis posuere. Morbi volutpat luctus turpis, '. 
    'in pretium augue pellentesque quis. Cras tempor, sem suscipit '. 
    'dapibus lacinia, dolor sapien ultrices est, eget laoreet nibh '. 
    'ligula at massa. Cum sociis natoque penatibus et magnis dis '. 
    'parturient montes, nascetur ridiculus mus. Phasellus nulla '. 
    'dolor, placerat non sem. Proin tempor tempus erat, facilisis '. 
    'euismod lectus pharetra vel. Etiam faucibus, lectus a '. 
    'scelerisque dignissim, odio turpis commodo massa, vitae '. 
    'tincidunt ante sapien non neque. Proin eleifend, lacus et '. 
    'luctus pellentesque;odio felis.'; 

上面的代碼(帶有大字符串)崩潰的Apache但工程,如果我在命令行上運行PHP。

在我的程序的其他地方,我使用preg_replace沒有問題的更大的字符串,所以我猜它的正則表達式淹沒PHP/Apache。

那麼,有沒有辦法'修復'的正則表達式,所以它在Apache的大字符串上工作,或者有另一種更安全的方式來做到這一點?

我在Windows XP SP3上使用PHP 5.2.17和Apache 2.0.64,如果有任何幫助的話。 (不幸的是,升級PHP或Apache是​​不是現在的選項。)

回答

2

我建議本場比賽表現:

\b(?<!&)(?<!&#)\w+; 

...它匹配一系列字符(字母,數字和下劃線),這不是由之前符號(或符號後跟一個哈希符號),但後面跟着一個分號。

它分解意味着:

\b   # assert that this is a word boundary 
(?<!  # look behind and assert that you cannot match 
&   # an ampersand 
)   # end lookbehind 
(?<!  # look behind and assert that you cannot match 
&#   # an ampersand followed by a hash symbol 
)   # end lookbehind 
\w+   # match one or more word characters 
;   # match a semicolon 

替換字符串'$0 '

讓我知道這不適合你

當然,您還可以使用[a-zA-Z0-9]代替\w以避免匹配分號,但我不認爲這會給你帶來任何麻煩

此外,您可能需要躲避哈希符號以及(因爲那是正則表達式註釋符),像這樣:

\b(?<!&)(?<!&\#)\w+; 

編輯不知道,但我猜字當頭開始時的邊界會使它效率更高(因此不太可能導致服務器崩潰),所以我在表達式和分解中改變了這一點......

EDIT 2 ...以及爲什麼你的表達可能是使你的服務器崩潰多一點信息:Catastrophic Backtracking - 我認爲適用嗯....好的信息仍然

(?)

最後編輯,如果你正在尋找只是一個分號後添加一個空格,如果有尚未空格後(即加入的pellentesque;odio的情況下,但不是在pellentesque; odio的情況下,一個),然後添加一個額外的先行在最後,這將防止額外的不必要的空間被添加:

\b(?<!&)(?<!&\#)\w+;(?!\s) 
+0

這很好用!由於瀏覽器通常不會顯示它們,所以不必介意額外的空格(最終編輯),但是一個很好的接觸。 爲什麼我的RegEx頭痛的解決方案總是看起來那麼簡單?...-) :-) – Goozak 2012-04-05 11:47:43

+0

@Goozak喜歡很多東西,你必須知道工具的所有怪癖和能力,然後才能以優雅的方式使用它 - 某人使用錘子或許能夠用一次打擊(我已經看到完成了,但更多的時候打了一次龍頭),或者他們可能會以未完成的工作和一個真正受傷的拇指結束。他們也許能夠完成這項工作而不會給任何人留下深刻的印象 - 這一切取決於你想投入多少努力和練習,並在一定程度上你需要幫助你學習:) – 2012-04-05 17:17:23

+0

試圖抓住HTML像-這樣的實體(你的#是爲了這個,對不對?) - 這個#有問題:http://rubular.com/r/yM0shbE9i2不應該抓住最後三分之一,對吧? – Joan 2015-09-17 16:13:30

0

你可以使用一個負向後看:

preg_replace('/(?<=[^\d]);([^\s])/', '; \1', $text) 

沒有測試過,因爲我已經得到了手邊沒有電腦,但是這或者它的一個小的變化應該工作。

+1

'\ D'和'\ S'是速記用於'[^ \ d]'和'[^ \ S]'分別。 – Joey 2012-04-04 21:03:19

+0

我總是忘記他們。謝謝:) – ckruse 2012-04-04 21:05:33

+1

抓住我,如果我在這裏錯了,但不是一個_positive_ lookbehind? :-D - 它看起來像你試圖匹配一個分號後跟除空格以外的東西,只要_is_是一個字符,在這個分號之前_is不是一個數字......我不認爲它很有效 - 首先,你不允許使用_numbered_實體,如問題中使用的'‑';其次,它也會把';;'變成'; ;'或';-)'成''; - )',因此打破了所有可愛的小笑臉人們喜歡使用:-D – 2012-04-04 21:45:44

0

有了這樣的問題,回調可能會有所幫助。

(&(?:[A-Za-z_:][\w:.-]*|\#(?:[0-9]+|x[0-9a-fA-F]+)))?; 

擴展

(   # Capture buffer 1 
    &        # Ampersand '&' 
    (?: [A-Za-z_:][\w:.-]*   # normal words 
    | \#       # OR, code '#' 
     (?: [0-9]+      # decimal 
     | x[0-9a-fA-F]+    # OR, hex 'x' 
     ) 
    ) 
)?   # End capture buffer 1, optional 
;   # Semicolon ';' 

測試用例http://ideone.com/xYrpg

<?php 

$line = ' 
    Coca&#8209;Cola 
    Beverage;Food;Music 
'; 

$line = preg_replace_callback(
     '/(&(?:[A-Za-z_:][\w:.-]*|\#(?:[0-9]+|x[0-9a-fA-F]+)))?;/', 
     create_function(
      '$matches', 
      'if ($matches[1]) 
       return $matches[0]; 
      return $matches[0]." ";' 
     ), 
     $line 
    ); 
echo $line; 
?> 
+0

似乎有點矯枉過正,併產生一些關於未定義偏移的PHP通知 - 我是通知和警告的標尺... :-) – Goozak 2012-04-05 11:52:22

+0

@ Goozak - 它實際上對xml不起任何作用,忽略了PE的參考,並排除了許多名字中的U-chars。 Html只應該是'(&(?:[A-Za-z] [\ w:.-] * | \#(?:[0-9] + | [xX] [0-9a-fA-F] +)))?'不知道你的PHP警告,ideone顯示沒有問題。最後,是什麼讓你認爲你接受的答案是正確的? '\ w'不包括實體可以擁有的所有有效字符,更不用說職位了。最重要的是,你不能分解實體表達,把一半放在你不想要的,一半放在你做的事情上! '\ b(?<!&)(?<!&#)\w+;'不會匹配''this';'。或者您的要求不是真實的世界 – sln 2012-04-05 18:08:01

+0

如果您想綁定(忽略)';'前一個字符在它之前注入一個反向後置符號(&(?:[A-Za-z] [\ w:.-] * | \#(?:[0-9] + | [xX] [0-9a (&(?:[A-Za-z] [\ w:.-] * | \#( ?:???![0-9] + | [XX] [0-9A-FA-F] +)))(<= \ S);(\ S)' – sln 2012-04-05 18:33:53

相關問題