外字かどうかを判定するというのは結構ある。まー文字のコード範囲がE000~F8FFかどうか見ればいいだけなので、割とすぐ実装できるし、調べればすぐ出てくる。

問題は、文字が割り当てられている外字かどうかを判定しようとしたとき。割り当てられてない文字なんか入ってくるわけないと思いたくなりますが、問題はデータ移行の時。

旧システム、特に他ベンダーから移行するとか、ホストから移行するみたいな場合、外字が入ってるともー発狂したくなる状況に陥りがち。

だいたい、コードの変換表とか作ったりして対応していくと思いますが、それでも変換が間違っていたり色々問題は出てくる。そーゆー時に、外字領域のコードなんだけど、字に割当たってないコードが来た時にどう見つけるかという事です。

字に割当たっていない時は外字のフォントファイルの作られ方によると思うけど、だいたい「・」になる。場合によっては「 」空白だったり、「□」だったりする。字は全部同じ形になるけど、コード的には全部違うので、例えば「”字”=”字”」みたいな比較は不可能。

文字コードで比較できないなら、変換後の字形を比較するしかない。

とゆーことで色々試した結果、↓のコードが出来上がりましたとさ。

とりあえず割り当てられてない文字コードを指定して一文字画像として描画する。あとは調べたいデータの文字を一文字ずつ画像に描画して、画像同士を比較する。比較には画像のハッシュを計算して、それを比較する方法を使用。

とりあえず、比較のベースにする1文字を画像として描画してハッシュを取得。

Encoding unicode = Encoding.GetEncoding("UTF-16");
//比較のベースにする文字コード
short baseChar = Convert.ToInt16("E123", 16);
byte[] baseCharByte = BitConverter.GetBytes(baseChar);
//UTF-16で文字に変える
string baseCharStr = unicode.GetString(baseCharByte);

Font fnt = new Font("MS 明朝", 11);
Bitmap canvasBaseChar = new Bitmap(20, 20);//画像のサイズ
Graphics g = Graphics.FromImage(canvasBaseChar);
g.FillRectangle(Brushes.White, 0, 0, canvasSize, canvasSize);//白で塗りつぶしておく
g.DrawString(baseCharStr, fnt, Brushes.Black, 0, 0);//文字を描画
g.Dispose();

//ベースの文字画像のMD5を取得
BitmapData bmpdata = canvasBaseChar.LockBits(new Rectangle(0, 0, canvasBaseChar.Width, canvasBaseChar.Height), ImageLockMode.ReadWrite, canvasBaseChar.PixelFormat);
IntPtr ptr = bmpdata.Scan0;
int bytes = bmpdata.Stride * canvasBaseChar.Height;
byte[] rgbValues = new byte[bytes];
Marshal.Copy(ptr, rgbValues, 0, bytes);
byte[] baseHash = new MD5CryptoServiceProvider().ComputeHash(rgbValues);

あとはデータの文字列を一文字ずつ同じように画像にしてハッシュを計算してベースと比較する。

Bitmap compareCanvas = new Bitmap(20, 20);
Graphics g = Graphics.FromImage(compareCanvas);
g.FillRectangle(Brushes.White, 0, 0, 20, 20);
g.DrawString(pStr, fnt, Brushes.Black, 0, 0);
g.Dispose();

BitmapData bmpdata = compareCanvas.LockBits(new Rectangle(0, 0, compareCanvas.Width, compareCanvas.Height), ImageLockMode.ReadWrite, compareCanvas.PixelFormat);
IntPtr ptr = bmpdata.Scan0;
int bytes2 = bmpdata.Stride * compareCanvas.Height;
byte[] rgbValues2 = new byte[bytes2];
Marshal.Copy(ptr, rgbValues2, 0, bytes2);
byte[] hash2 = new MD5CryptoServiceProvider().ComputeHash(rgbValues2);

bool bEqual = false;
if (hash2 != null && hash2.Length == baseHash.Length)
{
    int i = 0;
    while ((i < hash2.Length) && (hash2[i] == baseHash[i]))
    {
        i += 1;
    }
    if (i == hash2.Length)
    {
        bEqual = true;
    }
}
if (!bEqual)
{
    Console.WriteLine("違う字形");
}
else
{
    Console.WriteLine("同じ字形");
}

画像のハッシュを計算して比較するロジックはココのブログのロジックを使わせてもらいました。