UTF-8 to UTF-16 example2
更新日2007年01月26日

UTF-8 to UTF-16 example2
UTF-8 to UTF-16 への文字コード変換のサンプル実装。(Windows APIを使用しない)
MinGW + gcc 環境において動作確認してあります。

Example Code
以下の例は、Utf8ToUtf16()の実装の例です。 [utf8_to_utf16_2.zip]
/*
 * file:Utf8ToUtf16_2.c
 */
/** @file
 * @brief UTF-8 → UTF-16 への変換実装サンプルプログラム。
 * 
 * UTF-8 → UTF-16 への変換実装サンプルプログラムその2。(Windows APIを使用しない)
 * UTF-8にて用意された"data.txt"ファイルを読み込み、
 * UTF-16へと変換、
 * "out.txt"ファイルへと出力します。
 * ファイルへ出力時には、ファイルの頭に2バイトのデータ
 * 0xff 0xfe を出力します。
 */
/* ビットパターン
 * ┌───────┬───────┬───────────────────────────┬──────────────────┐
 * │フォーマット  │Unicode       │UTF-8ビット列                                         │Unicodeビット列                     │
 * ├───────┼───────┼───────────────────────────┼──────────────────┤
 * │1バイトコード │\u0~\u7f     │0aaabbbb                                              │00000000 0aaabbbb                   │
 * ├───────┼───────┼───────────────────────────┼──────────────────┤
 * │2バイトコード │\u80~\u7ff   │110aaabb 10bbcccc                                     │00000aaa bbbbcccc                   │
 * ├───────┼───────┼───────────────────────────┼──────────────────┤
 * │3バイトコード │\u800~\uffff │1110aaaa 10bbbbcc 10ccdddd                            │aaaabbbb ccccdddd                   │
 * ├───────┼───────┼───────────────────────────┼──────────────────┤
 * │4バイトコード │--------------│11110??? 10?????? 10?????? 10??????                   │未対応                              │
 * ├───────┼───────┼───────────────────────────┼──────────────────┤
 * │5バイトコード │--------------│111110?? 10?????? 10?????? 10?????? 10??????          │未対応                              │
 * ├───────┼───────┼───────────────────────────┼──────────────────┤
 * │6バイトコード │--------------│1111110? 10?????? 10?????? 10?????? 10?????? 10?????? │未対応                              │
 * └───────┴───────┴───────────────────────────┴──────────────────┘
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#define DEFAULT_BUFF_LEN 512

long min(long a, long b)
{
    long c;
    c = (a < b)?(a):(b);
    return c;
}

int Utf8ToUtf16(wchar_t *dest, size_t dest_size, char *src, size_t src_size);

int main(int argc, char *argv[])
{
    char    instr[DEFAULT_BUFF_LEN];
    wchar_t outstr[DEFAULT_BUFF_LEN];
    wchar_t bom;
    char    outbuffbom[4];
    FILE    *pFile;
    long    sizeFile;
    size_t  countRead;
    int     iResult = 0;
    int     instr_size = 0;
    
    /*
     * ファイルより文字列を読み込み
     */
    memset(instr, 0, sizeof(instr));
    pFile = fopen("data.txt", "rb");
    if (pFile == NULL)
    {
        perror("ファイルが開けません: data.txt");
        return 1;
    }
    fseek(pFile, 0, SEEK_END);
    sizeFile = ftell(pFile);
    fseek(pFile, 0, SEEK_SET);
    countRead = fread(instr, min(sizeFile, DEFAULT_BUFF_LEN), 1, pFile);
    fclose(pFile);
    if (countRead == 0)
    {
        perror("ファイル読み込み失敗: data.txt");
        return 1;
    }
    
    instr_size = min(sizeFile, DEFAULT_BUFF_LEN) * countRead;
    
    /*
     * utf8:instr -> utf16:outstr 変換。
     */
    memset(outstr, 0, sizeof(outstr));
    iResult = Utf8ToUtf16(outstr, sizeof(outstr), instr, instr_size);
    if (iResult == FALSE)
    {
        perror("Utf8ToUtf16() Failed.\n");
        return 1;
    }
    printf("%d 文字変換しました。\n", iResult);
    
    /*
     * ファイルに文字列を書き込み。
     */
    pFile = fopen("out.txt", "wb");
    if (pFile == NULL)
    {
        perror("ファイルが開けません: out.txt");
        return 1;
    }
    bom = 0xfeff;
    memcpy(outbuffbom, &bom, sizeof(wchar_t));
    fwrite(outbuffbom, sizeof(wchar_t), 1, pFile);
    fwrite(outstr, iResult * 2, 1, pFile);
    fclose(pFile);
}

/**
 * 文字コードをUTF-8よりUTF16へと変換。
 * 
 * @param[out] dest 出力文字列UTF-16
 * @param[in]  dest_size destのサイズをワード単位で指定
 * @param[in]  src 入力文字列UTF-8
 * @param[in]  src_size 入力文字列のバイト数
 * 
 * @return 成功時には出力文字列の文字数を戻します。
 *         dest_size に0を指定し、こちらの関数を呼び出すと、変換された
 *         文字列を格納するのに必要なdestのサイズの文字数を戻します。
 *         関数が失敗した場合には、FALSEを戻します。
 */
int Utf8ToUtf16(wchar_t *dest, size_t dest_size, char *src, size_t src_size)
{
    const int nMaxReadSize = 6;
    size_t    countNeedsWords;
    size_t    cursor;
    char      chBuffer[nMaxReadSize];
    size_t    nReadDataSize;
    int       iCh1;
    int       sizeBytes;
    wchar_t   wcWork1, wcWork2, wcWork3;
    
    /*
     * 入力パラメータをチェック
     */
    if (dest_size == 0)
    {
        /* dest_size == 0 */
    }
    else
    {
        /* dest_size != 0 */
        if (dest == NULL)
        {
            /* Error -- Null Pointer Exception : dest */
            return FALSE;
        }
        if (dest_size < 0)
        {
            /* Error -- dest_size < 0 */
            return FALSE;
        }
    }
    if (src == NULL)
    {
        /* Error -- Null Pointer Exception : src */
        return FALSE;
    }
    if (src_size < 1)
    {
        /* Error -- src_size < 1 */
        return FALSE;
    }
    
    /*
     * BOMの除去
     */
    if ((*src == '\xef') && (*(src + 1) == '\xbb') && (*(src + 2) == '\xbf'))
    {
        /*
         * UTF-8のBOMはef bb bf
         * LE も BE も存在しないので、BOM付きの場合には除去。
         * BOMが無い場合はそのまま処理。
         */
        /* BOMがある場合にはBOMを無視する */
        src += 3;
    }
    
    countNeedsWords = 0;
    for (cursor = 0; cursor < src_size;)
    {
        /* srcより6バイトのデータを読み出し */
        nReadDataSize = (nMaxReadSize < (src_size - cursor))?(nMaxReadSize):(src_size - cursor);
        memcpy(chBuffer, (src + cursor), nReadDataSize);
        memset(chBuffer + nReadDataSize, 0, sizeof(chBuffer) - nReadDataSize);

        /* data size の調べる */
        iCh1 = ((int)(*chBuffer)) & 0x00ff;
        iCh1 = ~iCh1;    /* ビット反転 */
        if (iCh1 & 0x0080)
        {
            /* 0aaabbbb */
            sizeBytes = 1;
        }
        else if (iCh1 & 0x0040)
        {
            /* error(ここに出現してはいけないコード) */
            return FALSE;
        }
        else if (iCh1 & 0x0020)
        {
            /* 110aaabb 10bbcccc */
            sizeBytes = 2;
        }
        else if (iCh1 & 0x0010)
        {
            /* 1110aaaa 10bbbbcc 10ccdddd */
            sizeBytes = 3;
        }
        else if (iCh1 & 0x0008)
        {
            /* 未対応のマッピング(UTF-16に存在しないコード) */
            sizeBytes = 4;
        }
        else if (iCh1 & 0x0004)
        {
            /* 未対応のマッピング(UTF-16に存在しないコード) */
            sizeBytes = 5;
        }
        else if (iCh1 & 0x0002)
        {
            /* 未対応のマッピング(UTF-16に存在しないコード) */
            sizeBytes = 6;
        }
        else
        {
            /* error(ここに出現してはいけないコード) */
            return FALSE;
        }
        
        /*
         * dest_size をチェック
         */
        if (dest_size && (dest_size < (countNeedsWords + 1)))
        {
            /* Error : memory is not enough for dest */
            return countNeedsWords;
        }
        
        /* sizeBytes毎に処理を分岐 */
        if (dest_size) switch (sizeBytes)
        {
        case 1:
            /*
             * ビット列
             * (0aaabbbb)UTF-8 ... (00000000 0aaabbbb)UTF-16
             */
            *dest = ((wchar_t)(chBuffer[0])) & (wchar_t)0x00ff;     /* 00000000 0aaabbbb */
            dest++;
            break;
        case 2:
            /*
             * ビット列
             * (110aaabb 10bbcccc)UTF-8 ... (00000aaa bbbbcccc)UTF-16
             */
            wcWork1 = ((wchar_t)(chBuffer[0])) & (wchar_t)0x00ff;   /* 00000000 110aaabb */
            wcWork2 = ((wchar_t)(chBuffer[1])) & (wchar_t)0x00ff;   /* 00000000 10bbcccc */
            wcWork1 <<= 6;                                          /* 00110aaa bb?????? */
            wcWork1 &= 0x07c0;                                      /* 00000aaa bb000000 */
            wcWork2 &= 0x003f;                                      /* 00000000 00bbcccc */
            *dest = wcWork1 | wcWork2;                              /* 00000aaa bbbbcccc */
            dest++;
            break;
        case 3:
            /*
             * ビット列
             * (1110aaaa 10bbbbcc 10ccdddd)UTF-8 ... (aaaabbbb ccccdddd)UTF-16
             */
            wcWork1 = ((wchar_t)(chBuffer[0])) & (wchar_t)0x00ff;   /* 00000000 1110aaaa */
            wcWork2 = ((wchar_t)(chBuffer[1])) & (wchar_t)0x00ff;   /* 00000000 10bbbbcc */
            wcWork3 = ((wchar_t)(chBuffer[2])) & (wchar_t)0x00ff;   /* 00000000 10ccdddd */
            wcWork1 <<= 12;                                         /* aaaa???? ???????? */
            wcWork1 &= 0xf000;                                      /* aaaa0000 00000000 */
            wcWork2 <<= 6;                                          /* 0010bbbb cc?????? */
            wcWork2 &= 0x0fc0;                                      /* 0000bbbb cc000000 */
            wcWork3 &= 0x003f;                                      /* 00000000 00ccdddd */
            *dest = wcWork1 | wcWork2 | wcWork3;                    /* aaaabbbb ccccdddd */
            dest++;
            break;
        case 4:
        case 5:
        case 6:
        default:
            /* ダミーデータ(uff1f)を出力 */
            *dest = (wchar_t)0xff1f;
            dest++;
            break;
        }
        countNeedsWords++;
        cursor += sizeBytes;
    }
    
    return countNeedsWords;
}