🦔

【PHP】往復変換の安全性が保証されていないレガシーエンコーディングの文字を調べる

2023/10/11に公開

レガシーエンコーディングの文字のなかには Unicode との往復変換(ラウンドトリップ)で別の文字に変わってしまうものがあります。

例として NEC、IBM の機種依存文字を挙げます。ほとんど等しいをあらわす近似記号(≒、U+2252)は Shift_JIS では2つ定義されます(0x81E00x8790)。往復変換すると 0x8790ox81E0 に置き換わっています。

var_dump(
    "\x81\xE0" === roundtrip("\x87\x90", 'cp932')
);

function roundtrip($char, $enc) {
    return mb_convert_encoding(mb_convert_encoding($char, 'utf-8', $enc), $enc, 'utf-8');
}

往復変換で置き換わってしまう文字をすべて調べてみます。

count_unsafe_chars('cp932');

function roundtrip($char, $enc) {
    return mb_convert_encoding(mb_convert_encoding($char, 'utf-8', $enc), $enc, 'utf-8');
}

function tohexupper($char) {
    return strtoupper(bin2hex($char));
}

function count_unsafe_chars($enc) {
    $count = 0;

    for ($cp = 0x1000; $cp < 0x10000; ++$cp) {

        $c = chr($cp >> 8).chr($cp & 0xff);

        if (!mb_check_encoding($c, $enc)) {
            continue;
        }

        $ret = roundtrip($c, $enc);

        if ($c !== $ret) {
            echo '[', tohexupper($c), ' ', tohexupper($ret), ']';
            ++$count;
        }
    }

    echo PHP_EOL, PHP_EOL;
    echo $enc, PHP_EOL;
    echo 'count:', $count, PHP_EOL;
}

実行すると398文字が変換されたことがわかります。

Discussion