Featured image of post PHPのマルチバイト関数へのいくつかの追加

PHPのマルチバイト関数へのいくつかの追加

PHPに付属していないマルチバイト関数をいくつか追加しています。

PHPにデフォルトで付属しているmbstring拡張モジュールは、UTF-8で符号化されたコンテンツを扱うためにmb_で始まるいくつかのマルチバイト関数を備えています。

例えばUTF-8符号化方式では、どちらも文字列の長さを返す関数が、strlen("焔華Honoka55")は14(BMP漢字1文字は3バイト)、mb_strlen("焔華Honoka55")は10(1漢字は1文字と数える)を返します。

しかし、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]);
}

使用例

以下のコードは文字列の2文字目を「○」に変更するものです。

1
echo mb_substr_replace('灰太狼', '○', 1, 1); // 「灰○狼」を出力

mb_fgetc

fgetc関数にもマルチバイト版がありません。この関数は、ファイルポインタから1文字取り出すものです。もちろん、正確には1バイトです。以下は、そのマルチバイト版(正確には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;
}

この関数はUTF-8の符号化に基づいて実装されており、文字の1バイト目とマスクをビットごとにANDを取って、1文字を読み込むために必要なバイト数を判断します。192はU+0080〜U+07FF(2バイト)、224はU+0800〜U+FFFF(3バイト)、240はU+10000〜U+1FFFFF(4バイト)に一致します。もし、ファイルの符号化方式がUTF-8ではなく、例えばShift JISの場合は、他の方法を使用しなければならないです。ファイルの符号化方式を自動的に判断するにはより複雑なアルゴリズムが必要であり、一つの関数だけでは実現できません。

使用例

ここでは、読み込むファイルの内容が「草😂」(U+8349 U+1F602)で始まるとします。

1
2
3
$fp = fopen($_FILES['file']['tmp_name'], 'r');
echo mb_fgetc($fp); // ファイルポインタの1文字目「草」を読み込んで出力
echo mb_fgetc($fp); // ファイルポインタの2文字目「😂」を読み込んで出力

他にも既存のマルチバイト版がない関数は多くありますが、もし遭遇した場合はそれぞれ個別に分析してください。

最終更新 2023年11月18号