3

開発中のサイトの 1 つで検索機能を完成させようとしています。検索結果には一致した項目の内容の抜粋しか表示されないため、検索結果内の検索用語を強調表示し、それらの検索用語を実際に含むテキストの一部のみを表示する必要があります。

私がやろうと思ったのは、データベースからコンテンツ全体を取得し、それを使用preg_replaceして検索用語の前後に要素を挿入<span>し、同時に用語の前後の最初の 10 単語だけを抽出することです。したがって、これはその正規表現部分です。

(?:.*?)((?:\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ただし、上記の正規表現は 1 つの列にのみ適用されます (この列を、上記の正規表現を使用する列と呼びましょうcontent)。

したがって、私の問題は、列ではなく他の列で一致するたびに、content上記の正規表現がcontent列からすべてのテキストを削除することです。これは、(?:.*?)最初のサブパターンが一致し続け、次のサブパターンが見つからないためです。

この副作用なしで正規表現の本来の目的を実装する他の方法があるかどうか疑問に思っていました。私は現在preg_match_all、検索語とその前後の10語を一致させるために単純に使用することを考えています. すべての一致を繰り返し処理し、プレビュー テキストを手動で作成します。はい、これは適切な解決策ですが、正規表現の経験が浅いため、これに対する解決策を見つけようとする方がよいと思いました。

アップデート

contents2 つ以上の検索語を入力すると、空白になるだけであることに気付きました。それ以外は、完璧に機能します。なぜこれが起こっているのか、今ではわかりません。

更新 2

エコーpreg_last_error()すると、このエラーが発生しますPREG_BACKTRACK_LIMIT_ERROR。私は単語newpost検索語を使用します。

var_dump正規表現の Aと用語はこれを示しています。

@(?:.*?)((?:\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 段落の lorem ipsum です。このタスクには、より良い正規表現を見つける必要があると思います。

更新 4

サブパターンを使用Once-Onlyすると、問題が解決します。Once-Only詳細はわかりませんが、PHPマニュアルを読み直して、サブパターンがバックトラックが多すぎるのに役立つという部分を読んだだけです。これは新しい正規表現です:

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

しかし、より良い正規表現の提案はまだ受け付けています。ありがとう!

4

1 に答える 1

1

バックトラッキングの制限に達して問題が発生している場合は、通常、1 回限りのサブパターンを調べます。

ただし、この場合、主な問題は(?:.*?)が続いているよう(?:\w+\W+){0,10}です。たとえば、'hello world!' という文字列を考えてみましょう{0,10}。これは、次のすべての 2 つのパターンに一致します。

  • '' と 'こんにちは'
  • 'h' と 'ello'
  • 「彼」と「llo」
  • 「ヘル」と「ロ」
  • 「地獄」と「お」
  • 「こんにちは」と「世界!」
  • 「こんにちは w」と「orld!」
  • 「ハローヲ」と「rld!」
  • 「ハローワー」と「ld!」
  • 「こんにちは世界」と「d!」

この冗長なバックトラッキングをブロックする最も簡単な方法は\b、サブパターンの後に単語境界チェック ( )を追加すること(?:.*?)です。これにより、これらの潜在的な一致が

  • '' と 'こんにちは'
  • 「こんにちは」と「世界!」

編集: 1 回限りのサブパターンがここで機能しない理由の例を次に示します。

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

この例では、結果 'xc' が期待されますが、サブパターンは貪欲に 'ba' に一致し、その後バックトラックしないため、一致が失われます。パターンを ungreedy にすることもできますが、サブパターンの '' に一致した後は決してバックトラックしないため、結果 'bxc' が得られます。

于 2012-07-18T09:10:27.810 に答える