PHP使用ltrim、rtrim以及trim处理中文时遇到的“坑”

原创
2021/09/27 10:47
阅读数 137

说坑有点过分,更多的是自己写代码的时候,没有考虑全面,导致自己踩坑了。

案例复现

$source = 'CSDN科技';
$source = rtrim($source, "】");

发现经过rtrim处理的字符串无法入库,检查后发现数据库报错信息:

General error: 1366 Incorrect string value: ‘xxxxxxx’ for column xxxxx

其实看到这个报错,就能立马意识到问题所在:字符串被不正确的截取,导致数据库驱动无法识别为正确的UTF8编码的文本。
而导致这问题的根本原因是rtrim以及一系列的方法是二进制安全的。
我们先把最后一个字符的字节码打印出来:

var_dump(unpack('C*', '技'));
var_dump(unpack('C*', '】'));
//结果如下
array(3) {
    [1]=> int(230) [2]=> int(138) [3]=> int(128) } //技
array(3) {
    [1]=> int(227) [2]=> int(128) [3]=> int(145) } //】

其中的端倪:的最后一个字节和的第二个字节相同。
trim方法是二进制安全的,会把当做三个字符去递归处理原始文本,导致的最后一个字节被截取,只剩两个字节,已经不是原来的了,无法识别。
也就是说,只要原始字符串的尾部字节(包括递归处理后)包含在trim的第二个参数内,都会被依次递归处理。
下面这段代码跟我们最开始写的那段没有本质区别。

$source = 'anlige';
$source = rtrim($source, "aeb");

解决方案

封装三个mb开头方法,去处理多字节编码的文本,逻辑比较暴力,代码有待优化。
注意:封装的$char参数是单个字符,有兴趣也可以修改成递归处理多个字符。

if(!function_exists('mb_ltrim')){
    function mb_ltrim($str, $char){
        if(empty($str)) return '';
        while (mb_substr($str, 0, 1) == $char){
            $str = mb_substr($str, 1);
        }
        return $str;
    }
}
if(!function_exists('mb_rtrim')){
    function mb_rtrim($str, $char){
        if(empty($str)) return '';
        while (mb_substr($str, -1, 1) == $char){
            $str = mb_substr($str, 0, -1);
        }
        return $str;
    }
}
if(!function_exists('mb_trim')){
    function mb_trim($str, $char){
        return mb_rtrim(mb_ltrim($str, $char), $char);
    }
}

总结

1、PHP大部分方法都是二进制安全的,因此在使用的时候要注意。
2、并不是二进制安全的方法都会遇到这个问题,主要是trim系列的特殊处理方式:根据第二个参数传递的值,递归处理首尾字符。
3、遇到问题不可怕,能迅速想到问题的来源和解决方案才最重要的。

展开阅读全文
php
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部