Skip to main content
黑话筒

PHP substr 截取 UTF8字符串乱码的解决

PHP的substr方法可以按照字节来截取字符串, 对于英文来说是没问题的. 因为一个英文字符只占一个字节.

但是对于截取UTF8编码的中文就会存在问题.

比如:

var $str = '我爱twitter哈哈 ';
echo substr($str, 0, 1);

就会出现一个乱字符, 因为在php中, utf8编码的中文是占3个字节, 单单只取一个字节肯定是不行啦.

如果: echo substr($str, 0, 3)就会出现"我"

echo substr($str, 0, 9) 会出现"我爱twi"

在中英文混排的时候, 这个函数就显得无能为力了.

google了一下, 发现很多人写了替代方法, 比如自己去实现一个substr, 里面写循环.

这样肯定效率低下, 直接用substr的复杂度是1, 而自己写循环, 复杂度就是n了.

后来找到了Wikipedia的条目, 发现原来3字节的utf8编码是有规律的.

一个非英文字符的3-bytes utf8 编码, 总是以224-239之间的char code开头的, 这就好办了.

思路是这样的:

先用substr截取, 判断截取结果的最后一位是不是在224-239之间, 如果是, 就说明目前取到了3-byte utf8编码的第一位, 那么只需要把计划截取的长度-1即可.

如果截取结果的最后一位之前一位在224-239之间, 就说明目前取到了3-byte utf8编码的第二位, 只需要将计划截取的长度-2即可.

下面是代码:

function csubstr($string, $beginIndex, $length){
  if(strlen($string) < $length){
    return substr($string, $beginIndex);
  }

  $char = ord($string[$beginIndex + $length - 1]);
  if($char >= 224 && $char <= 239){
    $str = substr($string, $beginIndex, $length - 1);
    return $str;
  }

  $char = ord($string[$beginIndex + $length - 2]);
  if($char >= 224 && $char <= 239){
    $str = substr($string, $beginIndex, $length - 2);
    return $str;
  }

  return substr($string, $beginIndex, $length);
}