Разделить текст на предложения

Как я могу разбить текст на массив предложений?

Пример текста:

Жарь мне бобра. Жарь мне бобра! Пожаришь мне бобра? Жарь меня Бобер нет. 4?! Жарь мне много бобров... Конец

Должен выводить:

0 => Fry me a Beaver.
1 => Fry me a Beaver!
2 => Fry me a Beaver?
3 => Fry me Beaver no. 4?!
4 => Fry me many Beavers...
5 => End

Я пробовал некоторые решения, которые я нашел на SO через поиск, но все они терпят неудачу, особенно в 4-м предложении.

/(?<=[!?.])./

/\.|\?|!/

/((?<=[a-z0-9)][.?!])|(?<=[a-z0-9][.?!]\"))(\s|\r\n)(?=\"?[A-Z])/

/(?<=[.!?]|[.!?][\'"])\s+/    // <- closest one

2 ответа

Решение

Поскольку вы хотите "разделить" предложения, почему вы пытаетесь сопоставить их?

Для этого случая давайте использовать preg_split ().

Код:

$str = 'Fry me a Beaver. Fry me a Beaver! Fry me a Beaver? Fry me Beaver no. 4?! Fry me many Beavers... End';
$sentences = preg_split('/(?<=[.?!])\s+(?=[a-z])/i', $str);
print_r($sentences);

Выход:

Array
(
    [0] => Fry me a Beaver.
    [1] => Fry me a Beaver!
    [2] => Fry me a Beaver?
    [3] => Fry me Beaver no. 4?!
    [4] => Fry me many Beavers...
    [5] => End
)

Объяснение:

Проще говоря, мы разделяемся на сгруппированные пробелы (ы) \ s + и делаем две вещи:

  1. (?<= [.?!]) Позитивный взгляд за утверждением, в основном мы ищем, есть ли точка или вопросительный знак или восклицательный знак за пробелом.

  2. (? = [az]) Позитивное предпросмотр вперед, поиск, если после пробела есть буква, это своего рода обходной путь для no. 4 проблема.

Я рекомендую выполнять поиск знаков препинания без просмотра назад, затем отпускать совпадающие символы (с помощью ), затем сопоставлять пробел, а затем искать заглавную букву, обозначающую начало следующего предложения.

Код: (Демо)

      $str = 'Fry me a Beaver. Fry me a Beaver! Fry me a Beaver? Fry me Beaver no. 4?! Fry me many Beavers... End';

var_export(
    preg_split('~[.?!]+\K\s+(?=[A-Z])~', $str, 0, PREG_SPLIT_NO_EMPTY)
);

Выход:

      array (
  0 => 'Fry me a Beaver.',
  1 => 'Fry me a Beaver!',
  2 => 'Fry me a Beaver?',
  3 => 'Fry me Beaver no. 4?!',
  4 => 'Fry me many Beavers...',
  5 => 'End',
)

Хотя это и не обязательно для строки образца, PREG_SPLIT_NO_EMPTYпредотвратит создание пустого элемента в конце массива, если строка заканчивается знаком препинания.

С использованием \Kв моем ответе требуется меньше возврата. Это позволяет механизму регулярных выражений "шагать" по строке с большей эффективностью. В ответе Хамзы механизм регулярных выражений начинает сопоставляться каждый раз, когда есть пробел, затем, после того, как пробел сопоставляется, ему нужно читать назад, чтобы проверить пунктуацию, а затем, если это соответствует требованиям, ему нужно искать букву вперед.

В моем подходе механизм регулярных выражений начинает рассматривать совпадения только тогда, когда встречает один из перечисленных знаков препинания, и никогда не оглядывается назад. Есть много мест для сопоставления, но гораздо меньше соответствующих символов. По этим причинам в образце входной строки мой шаблон разбивает строку на 40 шагов , а шаблон Хамзы разбивает строку на 74 шага.

Такой эффективностью не стоит хвастаться для относительно небольших строк, но если вы анализируете большие тексты, то эффективность и минимизация возвратов становятся более важными.

Другие вопросы по тегам