Я хочу использовать str_word_count()
в строке UTF-8.
Это безопасно в PHP? Мне кажется, что это должно быть (особенно учитывая, что нет mb_str_word_count()
).
Но на php.net много людей путают воду, представляя свои собственные «многобайтовые совместимые» версии функции .
Поэтому, я думаю, я хочу знать …
Учитывая, что str_word_count
просто подсчитывает все последовательности символов в разделителе " "
(пробел), он должен быть безопасным для многобайтовых строк, даже если он не обязательно знает о символьных последовательностях, верно?
Существуют ли в UTF-8 эквивалентные «пробельные» символы, которые не являются ASCII " "
(пробелом)? #
Я думаю, что проблема может оказаться ложной.
Я бы сказал, вы правильно поняли. И действительно, в 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://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. Он также может быть довольно быстрым, если вы тщательно продутируете шаблон регулярного выражения.
Об «шаблоне ответа» – я не получаю требование «работать быстрее». Мы не говорим о долгих временах или о многом подсчетах, так что кому это нужно, если это займет несколько миллисекунд дольше или нет?
Однако 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 по существу является тем же, что и было предложено, за исключением того, что a) она уже возвращает счет, поэтому нет необходимости поставлять совпадения, что должно ускорить ее выполнение и b) на самом деле не должно быть резервного копирования 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. Я не видел никаких доказательств в вашей ссылке об обратном.
EDITED (чтобы показать новые подсказки): существует возможное решение с использованием 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
s ниже и быстрее, чем str_word_count
; str_word_count
работающий с символом SHY (как?); preg_word_count
работает быстрее (используя регулярное выражение word-separator preg_replace?). Предположим, что существует функция « my_word_count()
safe» 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, он разбивается и все на самом деле.