无中断空间和许多其他的UTF-8符号它的表示需要两个字节。;因此,在假定的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发布于 2013-10-11 11:20:19
这不是一个完整的答案,因为我没有说为什么有些PHP函数“在无效编码的字符串上完全失败”,而另一些则没有:参见@deceze at is的注释和@hakre的答案。如果您正在寻找str_word_count()的PCRE替代品,请参阅下面的preg_word_count()。
PS:关于“PHP 5在库中的行为一致性”的讨论,我的结论是PHP5并不是那么糟糕,但是我们已经创建了许多用户定义的包装(Fa)函数(请参阅的多样性!)或等待PHP6 :-)
谢谢@pebbl!如果我理解你的链接,PHP上缺少错误消息。所以一个可能的解决办法就是增加一个错误条件.我找到这里的情况 (它确保有效的utf8!)..。谢谢@deceze,因为它存在一个内建函数来检查这个条件(我在后面编辑了代码)。
将这些问题组合在一起,将解决方案转换为一个函数(编辑,感谢@hakre注释!),
function my_word_count($s,$triggError=true) {
if ( preg_match_all('/[-\'\p{L}]+/u',$s,$m) !== false )
return count($m[0]);
else {
if ($triggError) trigger_error(
// not need mb_check_encoding($s,'UTF-8'), see hakre's answer,
// so, I wrong, there are no 'misteious error' with preg functions
(preg_last_error()==PREG_BAD_UTF8_ERROR)?
'non-UTF8 input!': 'other error',
E_USER_NOTICE
);
return NULL;
}
}现在(在考虑了@hakre的答案后编辑),关于uniform behaviour:我们可以用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 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}]+/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;
}
}演示(尝试其他输入!):
$s = "THE UTF-8 NO-BREAK\xA0SPACE"; // a non-ASCII byte
print "\n-- str_word_count=".str_word_count($s,0);
print "\n-- preg_word_count=".preg_word_count($s);
$s = "THE UTF-8 NO-BREAK\xC2\xA0SPACE"; // utf8-encoded nbsp
print "\n-- str_word_count=".str_word_count($s,0);
print "\n-- preg_word_count=".preg_word_count($s);发布于 2013-10-11 19:53:56
好吧,我能感觉到您的失望,因为从str_word_count切换到preg_match_all并不容易。然而,你问这个问题的方式有点不精确,我还是试着回答它。不精确,因为你有大量错误的假设,你显然认为这是理所当然的(这发生在我们当中最好的人身上)。我希望我能纠正一下:
$s = "THE UTF-8 NO-BREAK\xA0SPACE"; // a non-ASCII byte
preg_match_all('/[-\'\p{L}]+/u',$s,$m);
var_dump($m); // empty! (corrupted)这个代码是错的。您在这里指责PHP没有给出警告或什么,但我必须承认,这里唯一的指责是“您”。PHP允许您检查错误。在您如此早地判断错误处理时必须发出警告之前,我必须提醒您,如何处理错误有不同的方法。有些处理是给出消息,另一种处理错误的方式是用返回值告知错误。如果我们访问preg_match_all的手册页面并查找返回值的文档,我们可以找到以下内容:
返回完全模式匹配的数目(可能为零),如果发生错误,则返回FALSE。
结尾的部分:
如果发生错误,则FALSE由我突出显示为
是一些常见的错误处理方式,用于向发生错误的调用代码发送信号。让我们回顾一下您认为不起作用的代码:
$s = "THE UTF-8 NO-BREAK\xA0SPACE"; // a non-ASCII byte
preg_match_all('/[-\'\p{L}]+/u',$s,$m);
var_dump($m); // empty! (corrupted)这段代码唯一显示的是,输入它的人(我猜是你),显然决定不做任何错误处理。这很好,除非那个人也抗议代码不起作用。
可悲的是,这是一个常见的用户错误,如果您编写脆弱的代码(例如,没有错误处理),不要期望它以可靠的方式工作。那是永远不会发生的。
那么,当你编程时,这需要什么呢?首先,你应该知道你使用的函数。这通常需要了解输入参数和返回值。你会发现这些信息通常被记录在案。使用手册。其次,您实际上需要关心返回值,并处理自己的错误。仅函数本身不知道发生错误意味着什么。这是个例外吗?那么您可能需要像在演示示例中那样执行异常处理。
<?php
/**
* @link http://stackoverflow.com/q/19316127/367456
*/
$s = "THE UTF-8 NO-BREAK\xA0SPACE"; // a non-ASCII byte
$result = preg_match_all('/[-\'\p{L}]+/u',$s,$m);
if ($result === FALSE) {
switch (preg_last_error()) {
case PREG_BAD_UTF8_ERROR:
throw new InvalidArgumentException(
'UTF-8 encoded binary string expected.'
);
default:
throw new RuntimeException('preg error occured.');
}
}
var_dump($m); // nothing at all corrupted...无论如何,这意味着您需要查看您所做的事情,了解它并编写更多的代码。没有魔法。没有窃听器。只是做了点工作。
您面前的另一部分可能是了解软件中的字符,但它更独立于PHP等具体编程语言,例如,您可以在这里进行介绍性阅读:
第一个是必读的,或者是必读的书签,因为它有很多要读的地方,但它解释得很好。
https://stackoverflow.com/questions/19316127
复制相似问题