Stay hungry, Stay foolish

0%

utf8字符串截取

对于字符串的截取,php里提供了不少的函数,普通的有substr,多字节的有mb_substr。我们常接触到的就是utf8字符的截取,一般的用mb_substr就可以搞定的。但是如果有特殊的需求,需要自己来写一个呢?

这里多少就要了解些utf8的原理了。

utf8是unix之父Ken Thompson提出的,具有以下性质:

  • 编码为U+0000U+007F的字符只占一个字节,就是0×000x7F,和ASCII码兼容

  • 编码大于U+007F的字符用2~6个字节表示,每个字节的最高位都是1,而ASCII码的最高位都是0,因此非ASCII码字符的表示中不会出现ASCII码字节(也就不会出现0字节)

  • 用于表示非ASCII码字符的多字节序列中,第一个字节的取值范围是0xC00xFD,根据它可以判断后面有多少个字节也属于当前字符的编码。后面每个字节的取值范围都是0×800xBF,见下面的详细说明

  • UCS定义的所有231个字符都可以用UTF-8编码表示出来

  • UTF-8编码最长6个字节,BMP字符的UTF-8编码最长三个字节

  • 0xFE和0xFF这两个字节在UTF-8编码中不会出现

UTF-8编码最长6个字节,按字节来区分的话可以分为6类,表示如下:

1
2
3
4
5
6
U-00000000 – U-0000007F: 0xxxxxxx
U-00000080 – U-000007FF: 110xxxxx 10xxxxxx
U-00000800 – U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 – U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 – U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 – U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

根据这些我们可以得出结论:

  • 第一个字节在0-127之间是一个字节

  • 在128-192的占两个字节

  • 在193-224之间的占三个字节……

知道这个原理了,写起来就简单的多:

  • 用strlen求出字符串的长度;

  • 遍历字符串,用ord求出当前字节的值;

  • 根据需求进行处理。

自己随便写了一个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function word_width_cut($string, $start = 0, $len = 0, $separator = '')
{
$str = "";
$begin = 0;
$count = 1;
$str_len = strlen($string);

for($i = 0; $i < $str_len; $i++) {
$ord = ord($string[$i]);
if (($count - ($start + 1)) >= $len) {
$str .= $separator;
return $str;
}else if ( $count >= $start) {
if ($ord >= 224 ){
$str .= substr($string, $i , 3);
$i += 2;
} elseif ($ord >= 192) {
$str .= substr($string, $i , 2);
$i += 1;
} else {
$str .= substr($string, $i , 1);
}
$count ++;
} else {
if ($ord >= 224 ){
$i += 2;
} elseif ($ord >= 192) {
$i += 1;
}
$count ++;
}
}
$str .= $separator;
return $str;
}

参考资料

据说打赏我的人,代码没有BUG