PHP str_word_count() многобайтово безопасно?

Я хочу использовать str_word_count() на строку UTF-8.

Это безопасно в PHP? Мне кажется, что так и должно быть (особенно если учесть, что нет mb_str_word_count()).

Но на php.net есть много людей, которые мутят воду, представляя свои собственные "многобайтовые совместимые" версии функции.

Я думаю, я хочу знать...

  1. При условии str_word_count просто считает все последовательности символов в разделенных " " (пробел), он должен быть безопасным для многобайтовых строк, даже если он не обязательно знает последовательности символов, верно?

  2. Есть ли в UTF-8 эквивалентные символы пробела, которые не являются ASCII? " " (пространство)?#

Я думаю, в этом и заключается проблема.

4 ответа

Решение

Я бы сказал, что ты угадаешь правильно. И действительно, в UTF-8 есть пробелы, которые не являются частью US-ASCII. Чтобы дать вам пример таких пространств:

И, возможно, также:

В любом случае, первый - "NO-BREAK SPACE" (U+00A0) - является хорошим примером, поскольку он также является частью кодировок Latin-X. И руководство по PHP уже дает подсказку, что str_word_count будет зависеть от локали.

Если мы хотим проверить это, мы можем установить языковой стандарт UTF-8 и передать недопустимую строку, содержащую \xA0 последовательность и, если это все еще считается символом разрыва слов, эта функция явно не безопасна в UTF-8, следовательно, не является многобайтовой (как и та, которая не определена в соответствии с вопросом):

<?php
/**
 * is PHP str_word_count() multibyte safe?
 * @link https://stackru.com/q/8290537/367456
 */

echo 'New Locale: ', setlocale(LC_ALL, 'en_US.utf8'), "\n\n";

$test   = "aword\xA0bword aword";
$result = str_word_count($test, 2);

var_dump($result);

Выход:

New Locale: en_US.utf8

array(3) {
  [0]=>
  string(5) "aword"
  [6]=>
  string(5) "bword"
  [12]=>
  string(5) "aword"
}

Как видно из этой демонстрации, эта функция полностью не справляется с обещанием локали, которое она дает на странице руководства (я не удивляюсь и не жалею об этом, чаще всего, если вы читаете, что функция зависит от локали в PHP, запускаете ее для своей жизни и находите такую). это не так), который я использую здесь, чтобы продемонстрировать, что он никоим образом не делает ничего, касающегося кодировки символов UTF-8.

Вместо этого для UTF-8 вы должны взглянуть на расширение PCRE:

PCRE хорошо понимает Unicode и UTF-8 в PHP в частности. Это также может быть довольно быстро, если вы тщательно создадите шаблон регулярного выражения.

Насчет "шаблона ответа" - я не получаю требование "работать быстрее". Мы не говорим здесь о долгом или большом количестве событий, так кого это волнует, если это займет несколько миллисекунд или нет?

Тем не менее, str_word_count работает с мягким дефисом:

function my_word_count($str) {
  return str_word_count(str_replace("\xC2\xAD",'', $str));
}

функция, которая соответствует утверждениям (но, вероятно, не быстрее, чем str_word_count):

function my_word_count($str) {
  $mystr = str_replace("\xC2\xAD",'', $str);        // soft hyphen encoded in UTF-8
  return preg_match_all('~[\p{L}\'\-]+~u', $mystr); // regex expecting UTF-8
}

Функция preg по сути та же самая, что уже предложена, за исключением того, что а) она уже возвращает счетчик, поэтому нет необходимости указывать совпадения, что должно ускорить его, и б) действительно не должно быть отступления iconv, IMO.


О комментарии:

Я вижу, что ваши PCRE-функции имеют меньшую производительность, чем моя preg_word_count(), потому что нужен str_replace, который вам не нужен: '~[^\p{L}\'-\xC2\xAD]+~u'работает нормально (!).

Я считал, что с другой стороны, замена строки удалит только многобайтовый символ, но ваше регулярное выражение будет иметь дело с \\xC2 а также \\xAD в любом порядке они могут появиться, что неправильно. Рассмотрим зарегистрированный знак, который является \xC2\xAE.

Однако теперь, когда я думаю об этом из-за того, как работает действующий UTF-8, это не будет иметь большого значения, поэтому его следует использовать одинаково хорошо. Таким образом, мы можем просто иметь функцию

function my_word_count($str) {
  return preg_match_all('~[\p{L}\'\-\xC2\xAD]+~u', $str); // regex expecting UTF-8
}

без необходимости в спичках или других заменах.

О str_word_count(str_replace("\xC2\xAD",'', $str));, если стабильно с UTF8, хорошо, но, похоже, нет.

Если вы прочитаете эту ветку, вы поймете, что str_replace безопасен, если вы придерживаетесь допустимых строк UTF-8. Я не видел никаких доказательств в вашей ссылке на обратное.

РЕДАКТИРОВАНИЕ (чтобы показать новые подсказки): есть возможное решение с использованием str_word_count() с PHP v5.1!

function my_word_count($str, $myLangChars="àáãâçêéíîóõôúÀÁÃÂÇÊÉÍÎÓÕÔÚ") { 
    return str_word_count($str, 0, $myLangChars);
}

но не на 100%, потому что я пытаюсь добавить в $ myLangChars \xC2\xAD ( символ SHY - SOFT HYPHEN), который должен быть компонентом слова на любом языке, и он не работает ( см.).

Другое, не очень быстрое, но полное и гибкое решение (извлечено здесь), основанное на библиотеке PCRE, но с возможностью имитации str_word_count() Поведение на недействительном UTF8:

 /**
  * Like str_word_count() but showing how preg can do the same.
  * This function is most flexible but not faster than str_word_count.
  * @param $wRgx the "word regular expression" as defined by user.
  * @param $triggError changes behaviour causing error event.
  * @param $OnBadUtfTryAgain when true mimic the str_word_count behaviour.
  * @return 0 or positive integer as word-count, negative as PCRE error.
  */
 function preg_word_count($s,$wRgx='/[-\'\p{L}\xC2\xAD]+/u', $triggError=true,
                          $OnBadUtfTryAgain=true) {
   if ( preg_match_all($wRgx,$s,$m) !== false )
      return count($m[0]);
   else {
      $lastError = preg_last_error();
      $chkUtf8 = ($lastError==PREG_BAD_UTF8_ERROR);
      if ($OnBadUtfTryAgain && $chkUtf8) 
         return preg_word_count(
            iconv('CP1252','UTF-8',$s), $wRgx, $triggError, false
         );
      elseif ($triggError) trigger_error(
         $chkUtf8? 'non-UTF8 input!': "error PCRE_code-$lastError",
         E_USER_NOTICE
         );
      return -$lastError;
   }
 }

(ШАБЛОН ОТВЕТ) помощь для щедрости!

(это не ответ, это помощь для щедрости, потому что я не могу изменить ни дублировать вопрос)

Мы хотим посчитать "слова реального мира" в тексте латинского языка UTF-8.

ДЛЯ БОЛЬНИ, НАМ НУЖНО:

  • функция, которая соответствует assertниже и быстрее чем str_word_count;
  • или же str_word_count работа с персонажем SHy (как?);
  • или же preg_word_count работать быстрее (используя preg_replace? регулярное выражение разделителя слов?).

УТВЕРЖДАЕТ

Предположим, что "многобайтовая безопасная" функция my_word_count() существует, тогда следующие утверждения должны быть верными:

assert_options(ASSERT_ACTIVE, 1);

$text = "1,2,3,4=0 (1 2 3 4)=0 (... ,.)=0  (2.5±0.1; 0.5±0.2)=0";
assert( my_word_count($text)==0 ); // no word there 

$text = "(one two,three;four)=4 (five-six se\xC2\xADven)=2";
assert( my_word_count($text)==6 ); // hyphen merges two words 

$text = "(um±dois três)=3 (àáãâçêéíîóõôúÀÁÃÂÇÊÉÍÎÓÕÔÚ)=1";
assert( my_word_count($text)==4 ); // a UTF8 case 

$text = "(ÍSÔ9000-X, ISÔ 9000-X, ÍSÔ-9000-X)=6"; //Codes are words?
assert( my_word_count($text)==6 ); // suppose no: X is another word

Все, что он делает, это подсчитывает количество пробелов или слов между ними. если вам интересно, вы можете просто сделать свою собственную функцию подсчета, используя разнесение и подсчет.

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

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