Разделить текст на отдельные слова

Я хотел бы разбить текст на отдельные слова, используя PHP. У вас есть идеи, как этого добиться?

Мой подход:

function tokenizer($text) {
    $text = trim(strtolower($text));
    $punctuation = '/[^a-z0-9äöüß-]/';
    $result = preg_split($punctuation, $text, -1, PREG_SPLIT_NO_EMPTY);
    for ($i = 0; $i < count($result); $i++) {
        $result[$i] = trim($result[$i]);
    }
    return $result; // contains the single words
}
$text = 'This is an example text, it contains commas and full-stops. Exclamation marks, too! Question marks? All punctuation marks you know.';
print_r(tokenizer($text));

Хороший ли это подход? У вас есть идеи по улучшению?

Заранее спасибо!


person caw    schedule 26.04.2009    source источник


Ответы (6)


Используйте класс \p{P}, который соответствует любому знаку пунктуации Юникода, в сочетании с классом пробелов \s.

$result = preg_split('/((^\p{P}+)|(\p{P}*\s+\p{P}*)|(\p{P}+$))/', $text, -1, PREG_SPLIT_NO_EMPTY);

Это разделит группу из одного или нескольких пробельных символов, но также втянет любые окружающие знаки препинания. Он также соответствует знакам пунктуации в начале или конце строки. Это различает такие случаи, как «не надо» и «он сказал« ой! »»

person moinudin    schedule 26.04.2009
comment
+1, не уверен, хотя, как это справится с äöüß. Регулярное выражение обычно классифицирует äöüß как словесные символы? - person Peter Perháč; 26.04.2009
comment
Спасибо. Это, вероятно, не сработает для английских текстов, но я также хочу извлечь немецкие умляуты (ä, ö, ü), ß и числа в строке. \W не извлечет Fri3nd, не так ли? - person caw; 26.04.2009
comment
Кажется, это не так, но обновленный ответ с чем-то похожим, что работает. - person moinudin; 26.04.2009
comment
Обновленный ответ работает с perl (на котором основано регулярное выражение php): $ echo äöüß, test | perl -e 'while (‹›) { if (/([\p{P}\s]+)/) { print $1\n; } }', - person moinudin; 26.04.2009
comment
Следует ли разделить не на don't и t? - person Eugene Yokota; 26.04.2009
comment
Обновил его, чтобы справиться с таким случаем :) - person moinudin; 26.04.2009
comment
Спасибо, marcog, все работает отлично! Но действительно ли он лучше, чем мой обновленный код выше? Собственно, в чем разница между нашими подходами? Один быстрее другого? - person caw; 26.04.2009
comment
В вашем подходе вы указываете символы без знаков препинания. Поэтому вы будете пропускать некоторые случаи, например. а. Зачем пытаться указать их вручную, когда весь набор знаков препинания юникода уже определен? И, как указал eed3si9n в моем первоначальном ответе, ваш разбивает такие слова, как «нет». - person moinudin; 26.04.2009
comment
@marcog Есть идеи, что было бы эквивалентом этого в Javascript? Я пытался сделать str.split(/((^\p{P}+)|(\p{P}*\s+\p{P}*)|(\p{P}+$))/);, но это не сработало. Пожалуйста помоги.. - person supersan; 20.04.2018
comment
Что делать, если я хочу удалить все номера? - person Jignesh Manek; 31.08.2018

Токенизация — strtok.

<?php
$text = 'This is an example text, it contains commas and full stops. Exclamation marks, too! Question marks? All punctuation marks you know.';
$delim = ' \n\t,.!?:;';

$tok = strtok($text, $delim);

while ($tok !== false) {
    echo "Word=$tok<br />";
    $tok = strtok($delim);
}
?>
person Eugene Yokota    schedule 26.04.2009
comment
Спасибо, я думаю, что эта функция делает это хорошо. - person caw; 26.04.2009
comment
Это не сработает, если вы получите : или ; или любой другой знак пунктуации, который вы не учли. - person moinudin; 26.04.2009
comment
@marcog, я добавил: и ;. Разве {P} не улавливает апостроф и дефис? - person Eugene Yokota; 26.04.2009
comment
Как насчет случаев такого цитирования? Мой обновленный ответ различает эти случаи. - person moinudin; 26.04.2009
comment
Отличная идея. Добавлен +1. Единственное, вокруг $delim = \n\t,.!?:;; должны быть двойные кавычки. С одинарными кавычками работает некорректно, тоже разбивается по букве n. - person Oleksiy Muzalyev; 02.12.2017

Я бы сначала сделал строку строчной, прежде чем разделить ее. Это сделало бы модификатор i и последующую обработку массива ненужными. Кроме того, я бы использовал сокращение \W для символов, отличных от слов, и добавил бы множитель +.

$text = 'This is an example text, it contains commas and full stops. Exclamation marks, too! Question marks? All punctuation marks you know.';
$result = preg_split('/\W+/', strtolower($text), -1, PREG_SPLIT_NO_EMPTY);

Изменить   Использовать символ Unicode properties вместо \W как предложил Марког. Что-то вроде [\p{P}\p{Z}] (знаки препинания и разделители) будет охватывать более конкретные символы, чем \W.

person Gumbo    schedule 26.04.2009
comment
Спасибо, идея выполнить strtolower() раньше очень хороша. Я буду использовать это. - person caw; 26.04.2009
comment
Какой цели служит strtolower(), если вы расстаетесь с \W? Вы хотите добавить модификатор шаблона u? Примечание для исследователей... \W не будет соответствовать символу подчеркивания. - person mickmackusa; 13.09.2020

вы также можете использовать функцию PHP strtok() для извлечения строковых токенов из вашей большой строки. вы можете использовать его следующим образом:

 $result = array();
 // your original string
 $text = 'This is an example text, it contains commas and full stops. Exclamation marks, too! Question marks? All punctuation marks you know.';
 // you pass strtok() your string, and a delimiter to specify how tokens are separated. words are seperated by a space.
 $word = strtok($text,' ');
 while ( $word !== false ) {
     $result[] = $word;
     $word = strtok(' ');
 }

см. дополнительную информацию о документации по php для strtok()

person farzad    schedule 26.04.2009
comment
в чем разница между этим и взорваться (' ', $ text); - person roopunk; 24.06.2015
comment
Пример кода в вопросе представляет собой токенизатор, мой ответ подразумевал, что PHP имеет встроенный токенизатор строк. Кроме того,explore() вернет все слова текста сразу, но с помощью strtok() у вызывающего абонента есть возможность прекратить поиск слов в тексте, как только будет выполнено желаемое условие. Кроме этого, я не могу придумать никакой другой разницы. - person farzad; 25.06.2015

Do:

str_word_count($text, 1);

Или, если вам нужна поддержка юникода:

function str_word_count_Helper($string, $format = 0, $search = null)
{
    $result = array();
    $matches = array();

    if (preg_match_all('~[\p{L}\p{Mn}\p{Pd}\'\x{2019}' . preg_quote($search, '~') . ']+~u', $string, $matches) > 0)
    {
        $result = $matches[0];
    }

    if ($format == 0)
    {
        return count($result);
    }

    return $result;
}
person Alix Axel    schedule 26.04.2009
comment
Спасибо, но это не сработает. Fri3nd не будет извлечен, но должен. - person caw; 26.04.2009
comment
Я не понимаю, почему Fri3nd должен быть извлечен. Убрали из массива, разбили на пт3 и нд (или подобные)? о.о - person David says reinstate Monica; 26.04.2009
comment
Если вы хотите рассматривать числа как слова, просто выполните str_word_count_Helper($string, 1, '0123456789'); - person Alix Axel; 26.04.2009

Вы также можете использовать метод взорваться: http://php.net/manual/en/function.explode.php

$words = explode(" ", $sentence);
person jfgrang    schedule 10.10.2012
comment
не работает с 2 или более последовательными пробелами. вы должны использовать foreach с explode(" ", $sentence) внутри if($word == "") continue;, чтобы избежать пустых слов. - person LucScu; 06.04.2017