PHPでテキストを文章に分割したいと考えています。私は現在、正規表現を使用していますが、これは最大 95% の精度をもたらし、より良いアプローチを使用して改善したいと考えています。Perl、Java、および C でそれを行う NLP ツールを見てきましたが、PHP に適合するものは見当たりませんでした。そのようなツールを知っていますか?
6 に答える
強化された正規表現ソリューション
Mr.
次のような略語の処理に注意を払うと仮定するとMrs.
、次の単一の正規表現ソリューションが非常にうまく機能します。
<?php // test.php Rev:20160820_1800
$split_sentences = '%(?#!php/i split_sentences Rev:20160820_1800)
# Split sentences on whitespace between them.
# See: http://stackoverflow.com/a/5844564/433790
(?<= # Sentence split location preceded by
[.!?] # either an end of sentence punct,
| [.!?][\'"] # or end of sentence punct and quote.
) # End positive lookbehind.
(?<! # But don\'t split after these:
Mr\. # Either "Mr."
| Mrs\. # Or "Mrs."
| Ms\. # Or "Ms."
| Jr\. # Or "Jr."
| Dr\. # Or "Dr."
| Prof\. # Or "Prof."
| Sr\. # Or "Sr."
| T\.V\.A\. # Or "T.V.A."
# Or... (you get the idea).
) # End negative lookbehind.
\s+ # Split on whitespace between sentences,
(?=\S) # (but not at end of string).
%xi'; // End $split_sentences.
$text = 'This is sentence one. Sentence two! Sentence thr'.
'ee? Sentence "four". Sentence "five"! Sentence "'.
'six"? Sentence "seven." Sentence \'eight!\' Dr. '.
'Jones said: "Mrs. Smith you have a lovely daught'.
'er!" The T.V.A. is a big project! '; // Note ws at end.
$sentences = preg_split($split_sentences, $text, -1, PREG_SPLIT_NO_EMPTY);
for ($i = 0; $i < count($sentences); ++$i) {
printf("Sentence[%d] = [%s]\n", $i + 1, $sentences[$i]);
}
?>
式に略語を簡単に追加したり、削除したりできることに注意してください。次のテスト段落があるとします。
これは文1です。文2!文3?文「4」。文「五」!文「6」?文「7」。文「エイト!」ジョーンズ博士は言った:「スミスさん、あなたには素敵な娘がいます!」TVAは大きなプロジェクトです!
スクリプトからの出力は次のとおりです。
Sentence[1] = [This is sentence one.]
Sentence[2] = [Sentence two!]
Sentence[3] = [Sentence three?]
Sentence[4] = [Sentence "four".]
Sentence[5] = [Sentence "five"!]
Sentence[6] = [Sentence "six"?]
Sentence[7] = [Sentence "seven."]
Sentence[8] = [Sentence 'eight!']
Sentence[9] = [Dr. Jones said: "Mrs. Smith you have a lovely daughter!"]
Sentence[10] = [The T.V.A. is a big project!]
必須の正規表現ソリューション
質問の作者は、上記の解決策は「多くのオプションを見落としている」とコメントし、十分に一般的ではありません。それが何を意味するのかはわかりませんが、上記の表現の本質は、あなたが得ることができるのと同じくらいクリーンでシンプルです。ここにあります:
$re = '/(?<=[.!?]|[.!?][\'"])\s+(?=\S)/';
$sentences = preg_split($re, $text, -1, PREG_SPLIT_NO_EMPTY);
どちらのソリューションも、句読点の終わりの後に引用符で終わる文を正しく識別することに注意してください。引用符で終わる文の一致を気にしない場合は、正規表現を次のように簡略化できます/(?<=[.!?])\s+(?=\S)/
。
編集:20130820_1000T.V.A.
正規表現とテスト文字列に追加されました(無視される別の句読点のある単語)。(PapyRefのコメントの質問に答えるため)
編集:20130820_1800正規表現を整理して名前を変更し、シバンを追加しました。また、末尾の空白でテキストが分割されないように正規表現を修正しました。
他の誰かの仕事のわずかな改善:
$re = '/# Split sentences on whitespace between them.
(?<= # Begin positive lookbehind.
[.!?] # Either an end of sentence punct,
| [.!?][\'"] # or end of sentence punct and quote.
) # End positive lookbehind.
(?<! # Begin negative lookbehind.
Mr\. # Skip either "Mr."
| Mrs\. # or "Mrs.",
| Ms\. # or "Ms.",
| Jr\. # or "Jr.",
| Dr\. # or "Dr.",
| Prof\. # or "Prof.",
| Sr\. # or "Sr.",
| \s[A-Z]\. # or initials ex: "George W. Bush",
# or... (you get the idea).
) # End negative lookbehind.
\s+ # Split on whitespace between sentences.
/ix';
$sentences = preg_split($re, $story, -1, PREG_SPLIT_NO_EMPTY);
このような略語のリストを作成します
$skip_array = array (
'Jr', 'Mr', 'Mrs', 'Ms', 'Dr', 'Prof', 'Sr' , etc.
それらを式にコンパイルします
$skip = '';
foreach($skip_array as $abbr) {
$skip = $skip . (empty($skip) ? '' : '|') . '\s{1}' . $abbr . '[.!?]';
}
最後にこのpreg_splitを実行して、文に分割します。
$lines = preg_split ("/(?<!$skip)(?<=[.?!])\s+(?=[^a-z])/",
$txt, -1, PREG_SPLIT_NO_EMPTY);
また、HTMLを処理している場合は、文の間のスペースをなくすタグが削除されるのを監視してください。このスティック<p></p>
を一緒に使用すると、解析が非常に困難になります。situations.Like
where.They
私はこの正規表現を使用していました:
preg_split('/(?<=[.?!])\s(?=[A-Z"\'])/', $text);
数字で始まる文では機能しませんが、誤検知もほとんどないはずです。もちろん、あなたが何をしているかも重要です。私のプログラムは現在使用しています
explode('.',$text);
正確さよりもスピードが重要だと判断したからです。
ローテクなアプローチとして、explode
.、!、および ? を使用して、一連の呼び出しをループで使用することを検討することをお勧めします。あなたの針として。これは、(ほとんどのテキスト処理と同様に) 非常にメモリとプロセッサを集中的に使用します。一連の一時配列と、見つかったすべての文が正しい順序で数値的にインデックス付けされた 1 つのマスター配列を持つことになります。
また、一般的な例外 ( Mr.やDr.などのタイトルの . など) をチェックする必要がありますが、すべてが配列内にあるため、これらのタイプのチェックはそれほど悪くないはずです。
これが速度とスケーリングの点で正規表現より優れているかどうかはわかりませんが、試してみる価値はあります。文章に分割したいこれらのテキストブロックの大きさはどれくらいですか?