Featured image of post PHP多字节函数的一些补充

PHP多字节函数的一些补充

本文补充一些PHP中不自带的多字节函数。

PHP默认自带的mbstring扩展中有一些以mb_开头的专门用于处理多字节字符的函数,专门用以处理UTF-8编码的内容。

例如,UTF-8编码下同样是返回字符串长度的函数,strlen("焰华Honoka55")的返回值是14(一个BMP汉字为三个字节),而mb_strlen("焰华Honoka55")的返回值是10(一个汉字算作一个字符)。

不过,mbstring扩展提供的函数种类并不丰富,下面是一些笔者数据库课设中用到而补充的函数。

mb_substr_replace

mbstring扩展中有mb_substr函数,但并没有substr_replace函数的“mb版”。

substr_replace函数的功能为替换字符串中子串,例如

1
echo substr_replace('nidema zai Nihon!','Shanghai',11,5);

上面的代码表示将'nidema zai Nihon!'从第11个字符起的5个字符替换为'Shanghai',将打印“nidema zai Shanghai!”。当然,对于Unicode字符,该非多字节函数无法按预期处理。

下面是该函数的多字节版本的一个可行的实现。

1
2
3
4
5
6
7
function mb_substr_replace($string, $replace, $offset, $length = null){
    preg_match_all('/./us', $string, $ar);
    preg_match_all('/./us', $replace, $rar);
    $length = is_int($length) ? $length : mb_strlen($string);
    array_splice($ar[0], $offset, $length, $rar[0]);
    return implode($ar[0]);
}

使用例

为保护个人隐私,需要将姓名的第二个字改为“*”,代码如下:

1
echo mb_substr_replace('灰太狼', '*', 1, 1); // 打印“灰*狼”

mb_fgetc

fgetc函数也没有多字节版。该函数从文件指针中获取一个字符。当然,确切来说是一个字节。下面我们来实现它的多字节版(准确来说,是UTF-8版)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function mb_fgetc($stream){
    $mask = [192, 224, 240];
    $ch = fgetc($stream);
    if(!$ch) return false;
    $buf = $ch;
    for($i = 0; $i < 3; $i++){
        if((ord($ch) & $mask[$i]) != $mask[$i]) break;
        $buf .= fgetc($stream);
    }
    return $buf;
}

这里令首字节与掩码按位与来判断读入一个字符需要读几个字节,192匹配U+0080~U+07FF(双字节),224匹配U+0800~U+FFFF(三字节),240匹配U+10000~U+1FFFFF(四字节),这是UTF-8编码下的实现。如果文件编码不是UTF-8,比如是GBK,则还需要换用其他的方法。而自动判断文件的编码方式需要更复杂的算法,没办法仅靠一个函数实现。

使用例

这里假设要读取的文件内容开头为“草😂”(U+8349 U+1F602)。

1
2
3
$fp = fopen($_FILES['file']['tmp_name'], 'r');
echo mb_fgetc($fp); // 读取并打印文件指针的第一个字符“草”
echo mb_fgetc($fp); // 读取并打印文件指针的第二个字符“😂”

还有很多函数没有可以直接用的多字节版,若遇到,可以就具体函数给出合适的相应解决方案。

最后更新于 2023年1月13号