Mesoscopic Programming

タコさんプログラミング専門

Programming Tips #033:UTF-8 => Shift-JIS 変換

概要

きのうテキストビュワーのテストしてたら、文字化けするテキストファイルがあったので調べたら文字コードUTF-8 だった。
そこで変換方法をググって見たが、なかなかそのものずばりのサイトが見つからなかった。
仕方がないから UTF-8 の仕様を調べて自分で作ってみた。
ファイルんの頭にシグネチャが無いタイプもあるらしいが、調べるのも大変なのでとりあえずシグネチャ付きのみ対応。

ソースコード
#include <windows.h>

BOOL ConvUTF8( LPCTSTR path, LPCTSTR path2 )
{
    static const BYTE BOM[ 3 ] = { 0xEF, 0xBB, 0xBF };

    BOOL    result = FALSE;
    HANDLE  hFile  = CreateFile( path, GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
    HANDLE  hFile2;
    DWORD   dwRead, dwWrite;
    char    buffer [ 3 + 1 ];
    wchar_t buffer2[ 3 + 1 ];
    INT     n;

    if ( hFile != INVALID_HANDLE_VALUE )
    {
        if ( ReadFile( hFile, buffer, sizeof BOM, & dwRead, NULL ) && dwRead == sizeof BOM && memcmp( buffer, BOM, sizeof BOM ) == 0 )
        {
            hFile2 = CreateFile( path2, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );

            if ( hFile2 != INVALID_HANDLE_VALUE )
            {
                for ( ;; )
                {
                    if ( ! ReadFile( hFile, buffer, 1, & dwRead, NULL ) || dwRead == 0 )
                    {
                        break;
                    }
                    else if ( ( buffer[ 0 ] & 0xF0 ) == 0xE0 )
                    {
                        n = 3;

                        ReadFile( hFile, & buffer[ 1 ], 2, & dwRead, NULL );
                    }
                    else if ( ( buffer[ 0 ] & 0xE0 ) == 0xC0 )
                    {
                        n = 2;

                        ReadFile( hFile, & buffer[ 1 ], 1, & dwRead, NULL );
                    }
                    else
                    {
                        n = 1;
                    }

                    n = MultiByteToWideChar( CP_UTF8, 0, buffer, n, buffer2, sizeof ( wchar_t ) * ( n + 1 ) );

                    if ( n > 0 )
                    {
                        n = WideCharToMultiByte( CP_ACP, 0, buffer2, n, buffer, sizeof buffer, NULL, NULL );

                        if ( n > 0 )
                        {
                            buffer[ n ] = '\0';

                            WriteFile( hFile2, buffer, n, & dwWrite, NULL );
                        }
                    }
                }

                SetEndOfFile( hFile2 );
                CloseHandle( hFile2 );

                result = TRUE;
            }
        }

        CloseHandle( hFile );
    }

    return result;
}
解説

UTF-8 は1文字が1~3バイトになるらしい。
1バイト目で1文字が何バイトか調べたら MultiByteToWideChar() 関数でいったんワイド文字に変換する。
変換後のワイド文字を WideCharToMultiByte() 関数でマルチバイト文字に変換する。
これだけ。

ファイルの終端

デバッグしてたら、何度やってもファイルにごみが残るのでさんざん悩んだのだが、
生成されたファイルをいったん消して、新規作成でやると残らない。
考えてみたらファイルの終端を設定していなかったことに気づいた。
どうやってファイル終端を設定するのかググっても分からなかったが、
MSDN のヘルプ見たら SetEndOfFile() 関数を見つけた。
問題がしょぼすぎてググってもヒットしなかったのだ。
くわばら、くわばら。

参考サイト

UTFとは何か


以上。