PHP str_word_count () многобайтово безопасно? - PullRequest
10 голосов
/ 28 ноября 2011

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

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

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

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

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

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

Это проблема, я думаю.

Ответы [ 4 ]

3 голосов
/ 10 октября 2013

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

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

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

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

<?php
/**
 * is PHP str_word_count() multibyte safe?
 * @link https://stackoverflow.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 в частности.Это также может быть довольно быстро, если вы тщательно создадите шаблон регулярного выражения.

1 голос
/ 16 октября 2013

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

Однако 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 функционируетwrost (производительность), чем мой 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.Я не видел никаких доказательств в вашей ссылке об обратном.

0 голосов
/ 09 октября 2013

РЕДАКТИРОВАНИЕ (чтобы показать новые подсказки): есть возможное решение, использующее 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
0 голосов
/ 28 ноября 2011

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

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

...