13

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

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

但是在 php.net 上,有很多人通过展示他们自己的“多字节兼容”版本的函数来搅浑水。

所以我想我想知道...

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

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

我猜这就是问题所在。

4

4 回答 4

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"
}

正如这个演示所示,该函数在手册页上给出的语言环境承诺上完全失败(我对此并不怀疑也不抱怨,最常见的是,如果你读到一个函数是 PHP 中特定于语言环境的,终生运行并找到一个那不是)我在这里利用它来证明它对 UTF-8 字符编码没有任何作用。

而对于 UTF-8,您应该查看 PCRE 扩展:

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

于 2013-10-10T07:23:06.750 回答
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考虑一个注册符号,即\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 稳定,是好的,但似乎不是

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

于 2013-10-16T09:07:29.970 回答
0

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

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

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

另一个,不是那么快,但完整且灵活的解决方案(从这里提取),基于 PCRE 库,但可以选择模仿str_word_count()non-valid-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 拉丁文本中的“真实世界单词”。

为了赏金,我们需要:

  • 符合以下asserts 且比 更快的函数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
于 2013-10-09T13:56:38.813 回答
-2

它所做的只是计算空格或中间单词的数量。如果你很好奇,你可以使用explode 和count 来制作你自己的计数函数。

每当找到 ascii 空间字节时,它就会分裂,并且它的全部内容都是如此。

于 2011-11-28T01:29:42.327 回答