Вопрос по php, text-segmentation, regex, nlp – Это просто распознает язык, но не дает никаких функций НЛП. Мне просто нужно разделить текст на предложения, а это не позволяет.

15

ел бы разделить текст на предложения в PHP. В настоящее время я использую регулярное выражение, которое дает точность ~ 95% и хотелось бы улучшить его, используя лучший подход. Я видел инструменты NLP, которые делают это в Perl, Java и C, но не видел ничего подходящего для PHP. Знаете ли вы о таком инструменте?

"боль", потому что это медленнее, чем сказать С? Это регулярное выражение, которое я использую:preg_split("/(?<!\..)([\?\!\.]+)\s(?!.\.)/",$text,-1, PREG_SPLIT_DELIM_CAPTURE); Какой подход вы бы порекомендовали? Noam
Будетgithub.com/bigwhoop/sentence-breaker библиотека какая-то польза для вас? SenG
Какое регулярное выражение вы используете? НЛП в PHP звучит так, будто это причинит вам кучу боли. fredley

Ваш Ответ

7   ответов
0

Создайте список сокращений, как это

$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 склеить становится намного сложнее разобрать.

Взорваться просто дует строку на куски на основеdelimiter, Если вы говоритеexplode(" ", "Where are my suspenders?") The delimiter is"" пустое место. PHP будетexplode Ваша строка на куски, когда он сталкивается с пустым пространством. В этом случае в результате четыре слова, которые хранятся вarray какkeys [0-3].delimiter может быть что угодно,&, #, -, :и т.п.preg_split это более сложный взрыватель, который включает в себя рядmetacharacters, switches, functions and expressions, как в примере выше. user723220
0

Я использовал это регулярное выражение:

preg_split('/(?<=[.?!])\s(?=[A-Z"\'])/', $text);

Не будет работать над предложением, начинающимся с числа, но также должно иметь очень мало ложных срабатываний. Конечно, то, что вы делаете, имеет значение. Моя программа сейчас использует

explode('.',$text);

потому что я решил, что скорость важнее точности.

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]);
}
?>

Обратите внимание, что вы можете легко добавлять или убирать сокращения из выражения. Учитывая следующий тестовый абзац:

Это первое предложение. Приговор два! Приговор три? Приговор "четыре". Приговор "пять"! Приговор "шесть"? Приговор "семь". Приговор "восемь!" Доктор Джонс сказал: «Миссис Смит, у вас прекрасная дочь!» Т.В.А. это большой проект!

Вот вывод из скрипта:

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_1000 добавленнойT.V.A. (другое пунктуированное слово, которое нужно игнорировать) для регулярного выражения и проверки строки. (чтобы ответить на вопрос с комментариями PapyRef)

Изменить: 20130820_1800 Приведенный в порядок и переименован в регулярное выражение и добавил shebang. Также исправлены регулярные выражения для предотвращения разделения текста на конечные пробелы.

@Noam - если вам нужно решение, основанное на машинном обучении, обновите свой вопрос. David Meister
@ giorgio79: Да, если «элипсис» состоит из трех точек подряд. Если вы говорите об одном символе Unicode, представляющем elipsis, то этот символ Unicode должен быть добавлен в класс символов, чтобы это регулярное выражение работало. ridgerunner
Это все еще очень прямой подход. Я ищу что-то общее, что было построено в процессе обучения. Ваше решение пропускает много вариантов. Noam
Обнаруживает ли это многоточие? "..." в конце... giorgio79
С помощью этого расширенного решения регулярных выражений, как я могу обнаружить слово "T.V.A"? Я делаю это| [t|T]\.[v|V]\.[a|A]\. # or "T.V.A", но это не работает LeMoussel
0

ть использования серииexplode вызовы в цикле, используя.,!, и? как твоя игла Это было бы очень требовательно к памяти и процессору (как и большинство текстовых процессоров). У вас будет куча временных массивов и один мастер-массив со всеми найденными предложениями, численно проиндексированными в правильном порядке.

Кроме того, вы должны проверить общие исключения (такие как. В названиях, таких какГ-н. а такжеД-р), но с учетом того, что все находится в массиве, эти типы проверок не должны быть такими уж плохими.

Я не уверен, что это лучше, чем регулярное выражение с точки зрения скорости и масштабирования, но это стоило бы попробовать. Насколько велики эти блоки текста, которые вы хотите разбить на предложения?

@Noam:explode() разбивает на простое совпадение строк без каких-либо регулярных выражений. Смысл ответа в том, что для вашего случая использования это должно быть достаточно просто, чтобы сделать это без регулярных выражений; то есть просто взорваться на каждом общем знаке препинания. Однако я согласен, что он не отвечает на ваш вопрос и даже не отвечает на то, что вы пытаетесь задать. Вы стремитесь к точности, а это совсем не то, на чем он фокусируется. (но если бы вы выбрали этот подход, я быstrtok() быть лучшим решением, чемexplode() из-за нескольких знаков препинания) Spudley
Это не отвечает на мой вопрос, потому что я ищу библиотеку, которая делает это для меня. Но можете ли вы объяснить разницу между использованием explode и preg_split? Noam
2

Незначительное улучшение на чужой работе:

$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);
Не могли бы вы объяснить, где вы на самом деле улучшились? Kaii
-1
Это просто распознает язык, но не дает никаких функций НЛП. Мне просто нужно разделить текст на предложения, а это не позволяет. Noam
0

В результате я получаю 2 предложения:

Г-н Ж. Дюжарден регле са Т.В.A. en esp. uniquement

Правильным результатом должно быть предложение:Г-н Ж. Дюжарден регле са Т.В.А. en esp. uniquement

и с нашим тестовым пунктом

string sText = "This is sentence one. Sentence two! Sentence three? Sentence \"four\". Sentence \"five\"! Sentence \"six\"? Sentence \"seven.\" Sentence 'eight!' Dr. Jones said: \"Mrs. Smith you have a lovely daughter!\" The T.V.A. is a big project!";

Результат

index: 0 sentence: This is sentence one.
index: 22 sentence: Sentence two!
index: 36 sentence: Sentence three?
index: 52 sentence: Sentence "four".
index: 69 sentence: Sentence "five"!
index: 86 sentence: Sentence "six"?
index: 102 sentence: Sentence "seven.
index: 118 sentence: " Sentence 'eight!'
index: 136 sentence: ' Dr. Jones said: "Mrs. Smith you have a lovely daughter!
index: 193 sentence: " The T.V.
index: 203 sentence: A. is a big project!

Код C #:

                string sText = "Mr. J. Dujardin régle sa T.V.A. en esp. uniquement";
                Regex rx = new Regex(@"(\S.+?
                                       [.!?]               # Either an end of sentence punct,
                                       | [.!?]['""]         # or end of sentence punct and quote.
                                       )
                                       (?<!                 # 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,
                                        | T\.V\.A\.             # or "T.V.A."
                                       )                    # End negative lookbehind.
                                       (?=|\s+|$)", 
                                       RegexOptions.CultureInvariant | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled);
                foreach (Match match in rx.Matches(sText))
                {
                    Console.WriteLine("index: {0}  sentence: {1}", match.Index, match.Value);
                }

Похожие вопросы