项目中,遇到了一个计算校验中文存储字节数的问题,预期是一个中文字符可以被计算为2个字节,使用的PHP内置方法是 mb_strwidth,但是,有这么一个测试字符串$test_str = "/:;()?@“”。,?!",按照预期的计算值应该为21,本应不能通过校验规则(校验规则是统计字节数超过20,则无法通过校验),然而,实际得到的统计结果却为19,导致通过校验流程后,其他后续处理流程在处理这个字符串后的报错问题,相关代码如下:
<?php
$test_str = '/:;()?@“”。,?!';
$str_length = mb_strwidth($test_str, 'UTF-8');
// 结果为19,预期结果应该为21
echo $str_length;
?>
问题原因是中文的前后双引号(“和”)的unicode码分别为\u201c以及\u201d,然而, mb_strwidth对于
字符 | 宽度 |
---|---|
U+0000 - U+0019 | 0 |
U+0020 - U+1FFF | 1 |
U+2000 - U+FF60 | 2 |
U+FF61 - U+FF9F | 1 |
U+FFA0 - | 2 |
在U+0020-U+1FFF范围内的字符宽度计算值为1,并不是通常中文和中文标点符号的计算值2,因此,导致了最终的计算值比预期少2的问题现象。我这边参照了前端同学的JS验证算法,做了如下调整:
<?php
$pattern = '/[^\x00-\xff]/u';
$replacement = '**';
$new_str = preg_replace($pattern, $replacement, $test_str);
$new_str_length = mb_strwidth($new_str, 'UTF-8');
// 结果为21
echo $new_str_length;
?>
目前测试下来,包括本测试例子在内,大部分的测试用例,均能得到预期的计算统计结果,然而,不清楚这样的调整能否适应更多的场景情形,还需要不断观察总结。