Разделить текст на предложения
Как я могу разбить текст на массив предложений?
Пример текста:
Жарь мне бобра. Жарь мне бобра! Пожаришь мне бобра? Жарь меня Бобер нет. 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 + и делаем две вещи:
(?<= [.?!]) Позитивный взгляд за утверждением, в основном мы ищем, есть ли точка или вопросительный знак или восклицательный знак за пробелом.
(? = [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 шага.
Такой эффективностью не стоит хвастаться для относительно небольших строк, но если вы анализируете большие тексты, то эффективность и минимизация возвратов становятся более важными.