我想在UTF-8字符串上使用str_word_count()
。
这在PHP中安全吗?在我看来它应该是(特别是考虑到没有mb_str_word_count()
)。
但是在php.net上有很多人通过presenting their own 'multibyte compatible' versions of the function混淆了水。
所以我想我想知道......
鉴于str_word_count
只计算由" "
(空格)分隔的所有字符序列,它应该在多字节字符串上是安全的,即使它不一定知道字符序列,
UTF-8中是否有等效的'空格'字符,不是ASCII " "
(空格)?#
这就是我猜的问题所在。
答案 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\xAD
(SHy - 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区域文本中计算“真实世界的单词”。
assert
以下且速度高于str_word_count
; str_word_count
使用SHy角色(如何?); preg_word_count
工作得更快(使用preg_replace?word-separator正则表达式?)。假设存在“多字节安全”函数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空格字节,它都会分裂,并且确实存在所有字节。