2012-07-18 21 views
3

我試圖在我正在開發的某個網站中完成搜索功能。由於我的搜索結果僅顯示匹配項目內容的摘錄,因此我想要做的是在搜索結果中突出顯示搜索字詞,並僅顯示實際包含這些搜索字詞的部分文字。放棄所有字符,但搜索字詞前後的前10個字

我想我會做的是從數據庫中獲取的全部內容,並使用preg_replace插入四處搜尋範圍,並在同一時間提取之前僅在第10個單詞和術語後<span>元素。因此,這是它的正則表達式的一部分:

(?:.*?)((?:\w+\W+){0,10})('.implode('|', $terms).')((?:\W*\w+\W+){0,10}) 

基本上,我嘗試使用非捕獲子模式「丟棄」,除了搜索詞前的前10個單詞的所有文字,然後前拿到10個字術語本身,然後是接下來的10個單詞。

這是preg_replace替換文本:

\\1<span class="search-term search-term-content">\\2</span>\\3... 

搜索條件正在通過MySQLMATCH()...AGAINST()MyISAM FULLTEXT多個列的indeces搜索。但是,上述正則表達式只能應用於一列(我們稱之爲使用上述正則表達式的列,content)。

所以我的問題是每當我在其他列匹配但不在content列上時,上面的正則表達式將從content列中刪除所有文本。這是因爲(?:.*?)子模式在一開始就會繼續匹配而不會發現下一個子模式。

我想知道是否有任何其他方式來實現沒有這種副作用的正則表達式的原始目的。我目前正在考慮簡單地使用preg_match_all來匹配搜索詞和前後10個詞。我只是遍歷所有匹配並手動構建預覽文本。是的,這是一個很好的解決方案,但鑑於我對正則表達式的經驗不足,我認爲我不妨嘗試找到解決方案。

UPDATE

我只注意到我只得到空白contents當我把2個或更多搜索項。除此之外,它完美的作品。我現在不知道爲什麼會發生這種情況。

更新2

Echo'ing preg_last_error(),我得到這個錯誤PREG_BACKTRACK_LIMIT_ERROR。我使用搜索字詞newpost

正則表達式的var_dump和條款顯示此:

@(?:.*?)((?:\w+\W+){0,10})(new|post)((?:\W*\w+\W+){0,10})@i 

array 
    0 => string 'new' (length=3) 
    1 => string 'post' (length=4) 

更新3

我以前Regex Coach走我走過的匹配模式,它似乎回溯後太多找不到(new|post)。目標文本只是一段隨機的3段文字。我想我需要爲這個任務找到更好的正則表達式。

UPDATE 4

使用Once-Only子模式解決了這個問題。雖然我不知道它的細節,但我只是重新閱讀PHP手冊並閱讀其中的一部分,子模式有助於回溯太多。這是新的正則表達式:

(?:.*?)((?>\w+\W+){0,10})('.implode('|', $terms).')((?:\W*\w+\W+){0,10}) 

但我仍然樂於提供更好的正則表達式的建議。謝謝!

+1

當一個搜索詞出現在另一個搜索詞的10個詞以內時,怎麼辦?通過這種方法,你將不會包括接下來的10個單詞。可以嗎?什麼時候搜索詞真的很頻繁?你可以回覆整個文本嗎?似乎你可能需要稍微昂貴的方法來做到這一點...... – Braiba 2012-07-18 07:35:19

+0

preg_replace是否返回空字符串或null?如果它返回null,那麼在你的模式中有一個錯誤,在這種情況下,你可能想要回應一下。首先想到的是,您需要preg_quote這些值,除非您已經將它們分解爲字母數字字符只在構建數組的過程中。 – Braiba 2012-07-18 08:00:10

+0

嗨!我更新了問題以指出錯誤。我同意搜索條件彼此太接近是一個問題。感謝您的評論!我不介意它在返回之前截斷內容,因而返回了整個文本。 – 2012-07-18 08:20:41

回答

1

如果您遇到了達到回溯極限的問題,您通常需要查看once-only subpatterns

但是在這種情況下,您的主要問題似乎是(?:.*?)之後是(?:\w+\W+){0,10}。以字符串'hello world!'爲例,現在忽略{0,10}。這兩種模式相匹配,因爲所有如下:

  • '' 和 '你好'
  • 'H' 和 'ELLO'
  • '他' 和 '激光剝離'
  • 'HEL' 'lo'
  • 'hell'and'o'
  • 'hello'and'world!'
  • 'hello w'and'orld!'
  • 'hello wo'and'rld!'
  • 'hello wor'and'ld!'
  • 'hello worl'and'd!'

阻斷這種冗餘回溯最簡單的方法是(?:.*?)子模式後添加一個字邊界校驗(\b)。這將這些潛在的匹配降至

  • 「」和「你好」
  • 「你好」和「世界!」

編輯:這就是爲什麼一個一次性的子模式將在這裏工作的例子:

preg_replace('/(?>[a-z]{0,2})a/','x','bac') 

在這個例子中,我們所期望的結果「XC」,但是子模式貪婪地匹配到'巴',然後再回來,從而錯過了比賽。我們可以使模式不確定,但然後我們會得到結果'bxc',因爲它在匹配子模式的''後不會回溯。

+0

我看,所以一個'(?:。*?)\ b((?:\ w + \ W +){0,10})('。implode('|',$ terms) '((?:\ W * \ w + \ W +){0,10})'現在就夠了嗎?任何想法,如果原始問題可以通過正則表達式解決,或者我應該手動解析文本? – 2012-07-18 09:22:25

+0

應該工作,或者至少幫助,但是我肯定會通過單步方法手動解析文本(以及使用正則表達式,但在多個階段)。這是一個有趣的問題,儘管...我個人可能會通過找到每個搜索詞的位置,圍繞它們來計算範圍(以字符而非單詞測量),摺疊任何重疊,然後輸出前五個或者這樣的範圍。 – Braiba 2012-07-18 10:38:04

相關問題