字符串损坏或preg_match错误?

Pet*_*uss 6 php utf-8

NO-BREAK SPACE和许多其他UTF-8符号 需要2个字节才能表示 ; 因此,在UTF8字符串的假设上下文中,非ASCII(> 127)的隔离(非xC2前面)字节是一个不可识别的字符......好吧,它只是一个布局问题(!),但它破坏了整个字符串?

如何避免这种"非预期的行为"?(它出现在某些功能中,而不是在其他功能中).

示例(仅生成非预期行为preg_match):

  header("Content-Type: text/plain; charset=utf-8"); // same if text/html
  //PHP Version 5.5.4-1+debphp.org~precise+1
  //using a .php file enconded as UTF8.

  $s = "THE UTF-8 NO-BREAK\xA0SPACE"; // a non-ASCII byte
  preg_match_all('/[-\'\p{L}]+/u',$s,$m);
  var_dump($m);            // empty! (corrupted)
  $m=str_word_count($s,1);
  var_dump($m);            // ok

  $s = "THE UTF-8 NO-BREAK\xC2\xA0SPACE";  // utf8-encoded nbsp
  preg_match_all('/[-\'\p{L}]+/u',$s,$m);
  var_dump($m);            // ok!
  $m=str_word_count($s,1);
  var_dump($m);            // ok
Run Code Online (Sandbox Code Playgroud)

Pet*_*uss 5

这不是一个完整的答案,因为我没有说为什么某些 PHP 函数“在无效编码的字符串上完全失败”,而其他函数则不然:请参阅问题评论中的 @deceze 和 @hakre 答案。\n如果您正在寻找 PCRE 替代品str_word_count(),请参阅preg_word_count()下面的内容。

\n\n

PS:关于“PHP5的内置库行为一致性”的讨论,我的结论是PHP5还不错,但是我们创建了很多用户定义的包装(fa\xc3\xa7ade)函数(参见多样性PHP 框架!)...或者等待 PHP6 :-)

\n\n
\n\n

谢谢@pebbl!如果我理解您的链接,则 PHP 上缺少错误消息。因此,我所图示的问题的一个可能的解决方法是添加一个错误条件...我在这里找到了该条件(它确保了有效的 utf8!)...并且感谢@deceze,记住存在一个用于检查此条件的内置函数(我之后编辑了代码)。

\n\n

将问题放在一起,将解决方案转换为函数(已编辑,感谢 @hakre 评论!),

\n\n
 function my_word_count($s,$triggError=true) {\n   if ( preg_match_all(\'/[-\\\'\\p{L}]+/u\',$s,$m) !== false )\n      return count($m[0]);\n   else {\n      if ($triggError) trigger_error(\n         // not need mb_check_encoding($s,\'UTF-8\'), see hakre\'s answer, \n         // so, I wrong, there are no \'misteious error\' with preg functions\n         (preg_last_error()==PREG_BAD_UTF8_ERROR)? \n              \'non-UTF8 input!\': \'other error\',\n         E_USER_NOTICE\n         );\n      return NULL;\n   }\n }\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

现在(在思考@hakre答案后进行编辑),关于统一行为:我们可以使用PCRE库开发一个合理的函数来模仿该str_word_count行为,接受错误的UTF8。对于此任务,我使用了@bobinceiconv提示

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

演示(尝试其他输入!):

\n\n
 $s = "THE UTF-8 NO-BREAK\\xA0SPACE"; // a non-ASCII byte\n print "\\n-- str_word_count=".str_word_count($s,0);\n print "\\n-- preg_word_count=".preg_word_count($s);\n\n $s = "THE UTF-8 NO-BREAK\\xC2\\xA0SPACE";  // utf8-encoded nbsp\n print "\\n-- str_word_count=".str_word_count($s,0);\n print "\\n-- preg_word_count=".preg_word_count($s);\n
Run Code Online (Sandbox Code Playgroud)\n