字符串损坏或preg
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-BREAKxA0SPACE"; // 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-BREAKxC2xA0SPACE"; // 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
这不是一个完整的答案,因为我没有说出为什么某些PHP函数“完全失败于无效编码的字符串”,而另一些则不是:在问题的评论和@hakre答案中查看@deceze。 如果您正在寻找str_word_count()
的PCRE替换,请参阅下面的preg_word_count()
。
PS:关于“PHP5的库中内置行为一致性”的讨论,我的结论是PHP5并没有那么糟糕,但是我们创建了很多用户定义的wrap(façade)函数(请参阅PHP-framworks的多样性!)。 ..或者等待PHP6 :-)
谢谢@pebbl! 如果我理解你的链接,那么PHP上缺少错误消息。 所以我解决问题的一个可能的解决方法是添加一个错误条件...我在这里找到条件(它确保有效的utf8!)...并感谢@deceze记住,存在一个内置函数来检查这种情况我后来编辑了代码)。
把问题放在一起,一个解决方案转化为一个函数(EDITED,感谢@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答案后进行编辑),讨论统一的行为 :我们可以用模拟str_word_count
行为的PCRE库开发一个合理的函数,接受错误的UTF8。 对于这个任务,我使用了@bobince iconv
提示:
/**
* 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-BREAKxA0SPACE"; // 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-BREAKxC2xA0SPACE"; // utf8-encoded nbsp
print "n-- str_word_count=".str_word_count($s,0);
print "n-- preg_word_count=".preg_word_count($s);
好吧,我可以有点感到失望,因为从str_word_count
切换到preg_match_all
并不容易。 然而,你问这个问题的方式有点不准确,我试着回答它。 不准确,因为你有大量错误的假设,你显然认为理所当然(这发生在我们最好的情况下)。 我希望我能纠正这一点:
$s = "THE UTF-8 NO-BREAKxA0SPACE"; // 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-BREAKxA0SPACE"; // 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-BREAKxA0SPACE"; // 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等具体编程语言,例如你可以在这里做一个介绍性阅读:
第一个是必须阅读或可能必须书签,因为它是很多的阅读,但它解释它非常好。
链接地址: http://www.djcxy.com/p/59329.html