检查一个字符串是否被编码为UTF-8
function seems_utf8($str) {
$length = strlen($str);
for ($i=0; $i < $length; $i++) {
$c = ord($str[$i]);
if ($c < 0x80) $n = 0; # 0bbbbbbb
elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb
elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb
elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb
elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb
elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b
else return false; # Does not match any model
for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80))
return false;
}
}
return true;
}
我从Wordpress得到了这段代码,我对这个知之甚少,但是我想知道这个函数到底是什么。检查一个字符串是否被编码为UTF-8
如果有人知道请帮帮我吗?
我需要清楚的关于上述代码的想法。如果逐行解释会更有帮助。
我用两种方法来检查,如果字符串是UTF-8(视情况而定):
mb_internal_encoding('UTF-8'); // always needed before mb_ functions, check note below
if (mb_strlen($string) != strlen($string)) {
/// not single byte
}
- 或 -
if (preg_match('!\S!u', $string)) {
// utf8
}
对于mb_internal_encoding - 由于一些未知对我来说,PHP中的bug(版本5.3-(未在5.3上测试过))将编码作为参数传递给mb_函数不起作用,并且需要在使用mb_函数之前设置内部编码。
该算法基本上检查字节序列是否符合您可以在Wikipedia article中看到的模式。
for
循环将遍历$str
中的所有字节。 ord
获取当前字节的十进制数。然后对这个数字进行一些属性的测试。
如果数字小于128(0x80),则它是单字节字符。如果它等于或大于128,则检查多字节字符的长度。这可以通过多字节字符序列的第一个字符来完成。如果第一个字节以110xxxxx
开头,则它是一个双字节字符; 1110xxxx
,它是一个三字节字符等。
我认为最隐秘的部分是像($c & 0xE0) == 0xC0
这样的表达式。这是为了检查二进制格式的数字是否有一些特定的模式。我会试着解释一下这个例子是如何工作的。由于我们针对该模式测试的所有数字都等于或大于0x80,因此第一个字节始终为1,因此该模式至少限制为1xxxxxxxx
。如果我们那么做逐位,并与11100000
(取0xE0)相比,我们得到这个结果如下:
1xxxxxxx
& 11100000
= 1xx00000
因此,在5位和6位(从右边看,指数开始在0)取决于我们目前的电话号码是什么为了有等于11000000
,第5位必须为0
和第6位必须为1
:
1xxxxxxx
& 11100000
≟ 11000000
↓↓
→ 110xxxxx
这意味着我们的许多其他位可以是任意的:110xxxxx
。这正是*文章中预测的双字节字的第一个字节的模式。
最后内部for
循环是检查多字节字符的下列字节的完整性。这些都必须以10xxxxxx
开头。
如果你对UTF-8有一点了解,这是一个非常简单的实现。
function seems_utf8($str) {
# get length, for utf8 this means bytes and not characters
$length = strlen($str);
# we need to check each byte in the string
for ($i=0; $i < $length; $i++) {
# get the byte code 0-255 of the i-th byte
$c = ord($str[$i]);
# utf8 characters can take 1-6 bytes, how much
# exactly is decoded in the first character if
# it has a character code >= 128 (highest bit set).
# For all <= 127 the ASCII is the same as UTF8.
# The number of bytes per character is stored in
# the highest bits of the first byte of the UTF8
# character. The bit pattern that must be matched
# for the different length are shown as comment.
#
# So $n will hold the number of additonal characters
if ($c < 0x80) $n = 0; # 0bbbbbbb
elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb
elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb
elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb
elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb
elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b
else return false; # Does not match any model
# the code now checks the following additional bytes
# First in the if checks that the byte is really inside the
# string and running over the string end.
# The second just check that the highest two bits of all
# additonal bytes are always 1 and 0 (hexadecimal 0x80)
# which is a requirement for all additional UTF-8 bytes
for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80))
return false;
}
}
return true;
}
顺便说一下。在PHP上,我认为这是一个比C函数慢50-100的因子,所以你不应该在长字符串和生产系统上使用它。
绊倒在这篇文章中,也有类似的问题.. mb_detect_encoding表明UTF-8,但mb_check_encoding返回false ...
解决它,我的解决办法是:
$cur_encoding = mb_detect_encoding($in_str) ;
if($cur_encoding == "UTF-8" && mb_check_encoding($in_str,"UTF-8"))
return $in_str;
else
return utf8_encode($in_str);
SRY无法发布正确的链接....
所以只是做'mb_strlen($字符串, 'UTF-8')'吨母鸡。 – 2015-07-31 19:49:53