是PHP str_word_count()多字节安全吗?

时间:2011-11-28 01:16:59

标签: php utf-8 utf

我想在UTF-8字符串上使用str_word_count()

这在PHP中安全吗?在我看来它应该是(特别是考虑到没有mb_str_word_count())。

但是在php.net上有很多人通过presenting their own 'multibyte compatible' versions of the function混淆了水。

所以我想我想知道......

  1. 鉴于str_word_count只计算由" "(空格)分隔的所有字符序列,它应该在多字节字符串上是安全的,即使它不一定知道字符序列,

  2. UTF-8中是否有等效的'空格'字符,不是ASCII " "(空格)?#

  3. 这就是我猜的问题所在。

4 个答案:

答案 0 :(得分: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://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"
}

作为this demo shows,该函数完全失败了它在手册页面上提供的语言环境承诺(我不想也不会对此抱怨,最常见的是如果你读到一个函数是特定于PHP的语言环境,运行你的生活,并找到一个不是我在这里利用的,以证明它绝不会对UTF-8字符编码做任何事情。

相反,对于UTF-8,您应该查看PCRE扩展名:

PCRE非常了解PHP中的Unicode和UTF-8。如果仔细制作正则表达式模式,它也可以非常快。

答案 1 :(得分:1)

关于“模板答案” - 我没有得到“更快地工作”的要求。我们在这里谈的不是很长时间,也不是很多,所以谁关心它是否需要更长的时间?

但是,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功能比我的功能更糟糕(性能)   preg_word_count()因为需要一个你不需要的str_replace:   '〜[^ \ p {L} \' - \ xC2 \ xAD] + ~u'工作正常(!)。

我认为不同的东西,字符串替换只会删除多字节字符,但是你的正则表达式会以任何可能出现的顺序处理\\xC2\\xAD这是错的。考虑一个registered sign,即\ 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,很好,但是seems is not

如果您阅读this thread,如果您坚持使用有效的UTF-8字符串,您就会知道str_replace是安全的。我没有在你的相反链接中看到任何证据。

答案 2 :(得分:0)

EDITED(显示新线索):使用PHP {v5.1的str_word_count()可能有一个解决方案!

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

但不是100%,因为我尝试添加到$ myLangChars \xC2\xADSHy - SOFT HYPHEN字符),它必须是任何语言的单词组件,并且不起作用see)。

另一个,不是那么快,但是complete and flexible solution (extracted from here),基于PCRE库,但有一个选项来模仿非有效-UTF8上的str_word_count()行为:

 /**
  * 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区域文本中计算“真实世界的单词”。

FOR BOUNTY,我们需要:

  • 符合assert以下且速度高于str_word_count;
  • 的函数
  • str_word_count使用SHy角色(如何?);
  • preg_word_count工作得更快(使用preg_replace?word-separator正则表达式?)。

ASSERTS

假设存在“多字节安全”函数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

答案 3 :(得分:-2)

所有它都计算空格的数量或两者之间的单词。如果你很好奇,你可以使用爆炸和计数来制作自己的计数功能。

无论何时找到ascii空格字节,它都会分裂,并且确实存在所有字节。