对于字符串的截取,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; }
|
参考资料