php句子边界检测

Noa*_*oam 15 php regex nlp text-segmentation

我想用PHP将文本分成句子.我目前正在使用正则表达式,它带来了约95%的准确度,并希望通过使用更好的方法来改进.我已经看过在Perl,Java和C中使用NLP工具,但没有看到任何适合PHP的工具.你知道这样的工具吗?

rid*_*ner 21

增强的正则表达式解决方案

假设你做一下小心处理: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]);
}
?>
Run Code Online (Sandbox Code Playgroud)

请注意,您可以轻松地添加或删除表达式中的缩写.鉴于以下测试段落:

这是第一句话.一句两句!判刑三?句子"四".句子"五"!句子"六"?句子"七".句子'八!' 琼斯博士说:"史密斯太太你有一个可爱的女儿!" 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);
Run Code Online (Sandbox Code Playgroud)

请注意,两种解决方案都能在结束标点符号后正确识别以引号结尾的句子.如果你不关心匹配以引号结尾的句子,那么正则表达式可以简化为:/(?<=[.!?])\s+(?=\S)/.

编辑:20130820_1000添加T.V.A.(要忽略的另一个被打断的单词)到正则表达式和测试字符串.(回答PapyRef的评论问题)

编辑:20130820_1800整理并重命名正则表达式并添加了shebang.还修复了正则表达式以防止在尾随空格上拆分文本.


Tra*_*rav 0

explode作为一种低技术含量的方法,您可能需要考虑在循环中使用一系列调用,使用 .、! 和 ? 作为你的针。这将非常占用内存和处理器(就像大多数文本处理一样)。您将拥有一堆临时数组和一个主数组,其中所有找到的句子都按正确的顺序进行数字索引。

另外,您必须检查常见的异常(例如Mr.Dr.等标题中的 . ),但由于所有内容都在数组中,这些类型的检查应该不会那么糟糕。

我不确定这在速度和扩展方面是否比正则表达式更好,但值得一试。您想要分解成句子的这些文本块有多大?