やんちのプログラミングメモ

こんにちは、こちらはやんちのウェブページです。
このページにはやんちがプログラミング中に使用するメモを載せて行く予定です。

ISO-2022-JP to UTF-16 への文字コード変換のサンプル実装。
MinGW + gcc 3.4.2 環境において動作確認してあります。

以下の例は、Iso2022jpToUtf16()の実装の例です。 [iso2022jp_to_utf16_c.zip]

/*
 * iso2022jp_to_utf16.c
 *
 *  Created on: 2008/10/24
 *      Author: yanch
 */
/** @file
 * @brief ISO-2022-JP → UTF-16 への変換実装サンプルプログラム。
 * 
 */
/*
 * 1B 24 40     ESC $ @     JIS C 6226-1978(旧JIS漢字)
 *                          範囲{firstByte(0x21~0x7e), secondByte(0x21~0x7e)}
 * 1B 24 42     ESC $ B     JIS X 0208-1983 または JIS X 0208:1990(新JIS漢字)
 *                          範囲{firstByte(0x21~0x7e), secondByte(0x21~0x7e)}
 * 1B 28 42     ESC ( B     ASCII
 *                          範囲{0x00~0x7f}
 * 1B 28 49     ESC ( I     JIS X 0201(半角カナ)
 *                          範囲{0x21~0x5f}
 * 1B 28 4A     ESC ( J     JIS X 0201-1976のラテン文字集合 (ISO/IEC 646の日本版)
 *                          範囲{0x20~0x7e}
 */
#define STRICT

#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>

#include "iso2022jp_to_utf16_share.h"
#include "iso2022jp_to_utf16_table_1B2440.h"
#include "iso2022jp_to_utf16_table_1B2442.h"
#include "iso2022jp_to_utf16_table_1B284A.h"

/********************************************************************/
#define CODE_TYPE_OLD_JIS       (1)
#define CODE_TYPE_NEW_JIS       (2)
#define CODE_TYPE_ASCII         (3)
#define CODE_TYPE_HANKAKU_KANA  (4)
#define CODE_TYPE_RATEN         (5)
#define CODE_TYPE_BYTE_SIZE1    (6)
#define CODE_TYPE_BYTE_SIZE2    (7)
#define CODE_TYPE_UNKNOWN       (-1)

/********************************************************************/
/**
 * バッファ長。
 * 一行毎の文字列を処理するのに必要なバイト数
 */
#define DEFAULT_BUFF_LEN    4096

#define DUMMY_CODE      (0xff1f)

/********************************************************************/
/**
 * _OPTIONS 構造体
 * プログラム起動オプションを格納する構造体
 */
struct _OPTIONS
{
    char    i[DEFAULT_BUFF_LEN];
    char    o[DEFAULT_BUFF_LEN];
};

typedef struct _OPTIONS T_OPTIONS;

/********************************************************************/
size_t bytelen(const char *string, size_t max_length);
char *fgets_for_iso2022jp(char *string, int n, FILE *stream);
int getCodeType(char src[], int currenMode);
int Iso2022jpToUtf16(wchar_t *dest, size_t dest_size, char *src
        , size_t src_size);
int printUsage();
wchar_t selectUnicodeFromNewJisTable(unsigned long iso2022jpCode);
wchar_t selectUnicodeFromOldJisTable(unsigned long iso2022jpCode);

/********************************************************************/
/**
 * メイン関数。
 * ここからプログラムの実行を開始します。
 * 
 * @param[in] argc 引数の数
 * @param[in] argv 引数へのポインタ
 * @return 戻り値(正常終了時は0を戻す)
 */
int main(int argc, char *argv[])
{
    wchar_t     bom                         = 0;
    int         convertCharCount            = 0;
    wchar_t     dest[DEFAULT_BUFF_LEN];
    size_t      dest_size                   = 0;
    int         i                           = 0;
    int         iResult                     = 0;
    char        key[DEFAULT_BUFF_LEN];
    int         n                           = 0;
    T_OPTIONS   options;
    FILE        *pInputFile                 = NULL;
    FILE        *pOutputFile                = NULL;
    char        src[DEFAULT_BUFF_LEN];
    size_t      src_size                    = 0;
    
    /*
     * ●ロケールを設定
     */
    _wsetlocale(LC_ALL, L"japanese");
    
    /*
     * ●入力パラメタチェック
     */
    if (argc < 5)
    {
        printUsage();
        return 0;
    }
    else
    {
        memset(&options, 0, sizeof(options));
        memset(key, 0, DEFAULT_BUFF_LEN);
        for (i = 1; i < argc; i++)
        {
            if (*argv[i] == '-')
            {
                strcpy(key, argv[i]);
            }
            else
            {
                if (strcmp(key, "-i") == 0)
                {
                    strcpy(options.i, argv[i]);
                }
                else if (strcmp(key, "-o") == 0)
                {
                    strcpy(options.o, argv[i]);
                }
                strcpy(key, "");
            }
        }
    }
    if (*(options.i) == '\0' || *(options.o) == '\0')
    {
        printUsage();
        return 0;
    }
    
    /* 
     * ●ファイルを開く
     */
    pInputFile = fopen(options.i, "rb");
    if (pInputFile == NULL)
    {
        fwprintf(stderr, L"ファイルが開けません: %s\n", options.i);
    }
    pOutputFile = fopen(options.o, "wb");
    if (pOutputFile == NULL)
    {
        fwprintf(stderr, L"ファイルが開けません: %s\n", options.o);
    }
    
    if ((pInputFile != NULL) && (pOutputFile != NULL))
    {
        bom = 0xfeff;
        fwrite((char *)&bom, sizeof(wchar_t), 1, pOutputFile);
        
        while (feof(pInputFile) == 0)
        {
            /* 
             * ●ファイル読込
             */
            memset(src, 0, sizeof(char) * DEFAULT_BUFF_LEN);
            n = DEFAULT_BUFF_LEN - 1;
            if (NULL == fgets_for_iso2022jp(src, n, pInputFile))
            {
                src[0] = '\0';
                break;
            }
            
            /* 
             * ●文字コード変換
             * ISO-2022-JP:src -> UTF-16:dest 変換
             */
            memset(dest, 0, sizeof(wchar_t) * DEFAULT_BUFF_LEN);
            dest_size = DEFAULT_BUFF_LEN - 1;
            src_size = bytelen(src, sizeof(src));
            iResult = Iso2022jpToUtf16(dest, dest_size, src, src_size);
            if (iResult == (-1))
            {
                fwprintf(stderr, L"Iso2022jpToUtf16() Failed.\n");
                
                if (pInputFile != NULL)
                {
                    fclose(pInputFile);
                    pInputFile = NULL;
                }
                if (pOutputFile != NULL)
                {
                    fclose(pOutputFile);
                    pOutputFile = NULL;
                }
                return 1;
            }
            
            convertCharCount += iResult;
            
            /* 
             * ●ファイル書込
             */
            fputws(dest, pOutputFile);
        }
    }
    
    /* 
     * ●ファイルを閉じる
     */
    if (pInputFile != NULL)
    {
        fclose(pInputFile);
        pInputFile = NULL;
    }
    if (pOutputFile != NULL)
    {
        fclose(pOutputFile);
        pOutputFile = NULL;
    }
    
    wprintf(L"%d 文字変換しました。\n", convertCharCount);
    
    return 0;
}

/********************************************************************/
/**
 * ゼロ終端バイト列の長さを調べます。
 * バイト列がmax_lengthよりも大きい場合は、max_lengthを戻します。
 * 
 * @param[in]   sting       入力バイト列
 * @param[in]   max_length  バイト列を調べる最大長
 * @return 長さ(byte数)
 */
size_t bytelen(const char *string, size_t max_length)
{
    size_t i = 0;
    
    if (string == NULL)
    {
        return 0;
    }
    
    for (i = 0; i < max_length; i++)
    {
        if (*(string + i) == '\0')
        {
            break;
        }
    }
    
    return i;
}

/********************************************************************/
/**
 * ストリームから文字列を読み込みます。
 * 
 * @param[out]  string  データの格納場所
 * @param[in]   b       格納するバイト数
 * @param[in]   stream  FILE構造体へのポインタ
 * 
 * @return 成功するとstringを返します。エラーが発生するか、ファイルの終端に達するとNULLを返します。
 */
char *fgets_for_iso2022jp(char *string, int n, FILE *stream)
{
    char    *buffer;
    size_t  count   = 0;
    size_t  size    = 0;
    
    buffer = string;
    size = 1;
    count = 1;
    
    while (n)
    {
        if (fread(buffer, size, count, stream) != count)
        {
            return NULL;
        }
        
        if (*buffer == 0x0a)
        {
            return string;
        }
        
        buffer++;
        n--;
    }
    
    return string;
}

/********************************************************************/
/**
 * 入力コードの種類を調べます。
 * コードの種類
 * CODE_TYPE_OLD_JIS:コードタイプは旧JIS
 * CODE_TYPE_NEW_JIS:コードタイプは新JIS
 * CODE_TYPE_ASCII:コードタイプはASCII
 * CODE_TYPE_HANKAKU_KANA:コードタイプは半角カナ
 * CODE_TYPE_RATEN:コードタイプはラテン
 * CODE_TYPE_BYTE_SIZE1:コードタイプはバイトサイズ1
 * CODE_TYPE_BYTE_SIZE2:コードタイプはバイトサイズ2
 * CODE_TYPE_UNKNOWN:コードタイプは不明
 * 
 * @param[in]   src             入力ISO-2022-JP文字列
 * @param[in]   currentMode     カレントの文字集合
 * @return      コードの種類
 */
int getCodeType(char src[], int currenMode)
{
    if (src == NULL)
    {
        return CODE_TYPE_UNKNOWN;
    }
    
    switch (src[0])
    {
    case 0x1b:
        switch (src[1])
        {
        case 0x24:
            switch (src[2])
            {
            case 0x40:  /* 1B 24 40 */
                return CODE_TYPE_OLD_JIS;
            case 0x42:  /* 1B 24 42 */
                return CODE_TYPE_NEW_JIS;
            default:
                break;
            }
            break;
        case 0x28:
            switch (src[2])
            {
            case 0x42:  /* 1B 28 42 */
                return CODE_TYPE_ASCII;
            case 0x49:  /* 1B 28 49 */
                return CODE_TYPE_HANKAKU_KANA;
            case 0x4a:  /* 1B 28 4A */
                return CODE_TYPE_RATEN;
            default:
                break;
            }
            break;
        default:
            break;
        }
        break;
    default:
        switch (currenMode)
        {
        case CODE_TYPE_ASCII:
        case CODE_TYPE_HANKAKU_KANA:
        case CODE_TYPE_RATEN:
            return CODE_TYPE_BYTE_SIZE1;
        case CODE_TYPE_OLD_JIS:
        case CODE_TYPE_NEW_JIS:
            return CODE_TYPE_BYTE_SIZE2;
        default:
            return CODE_TYPE_UNKNOWN;
        }
    }
    
    return CODE_TYPE_UNKNOWN;
}

/********************************************************************/
/**
 * 文字コードを ISO-2022-JP より UTF-16 へと変換。
 * 
 * @param[out] dest 出力文字列 UTF-16
 * @param[in]  dest_size destの文字数
 * @param[in]  src 入力文字列 ISO-2022-JP
 * @param[in]  src_size 入力文字列のバイト数
 * 
 * @return 成功時には出力文字列の文字数を戻します。
 *         dest_size に0を指定し、こちらの関数を呼び出すと、変換された
 *         文字列を格納するのに必要なdestの文字数を戻します。
 *         関数が失敗した場合には、(-1)を戻します。
 */
int Iso2022jpToUtf16(wchar_t *dest, size_t dest_size, char *src
        , size_t src_size)
{
    char                buffer[4];
    int                 codeType            = CODE_TYPE_UNKNOWN;
    int                 countNeedsWords     = 0;
    int                 currentMode         = CODE_TYPE_ASCII;
    int                 cursor              = 0;
    unsigned long       iso2022jpCode       = 0;
    const int           nMaxReadSize        = 3;
    int                 nReadDataSize       = 0;
    int                 sizeBytes           = 0;
    wchar_t             unicode             = 0;
    
    /*
     * 入力パラメータをチェック
     */
    if (dest_size)
    {
        /* dest_size != 0 */
        if (dest == NULL)
        {
            /* Error -- Null Pointer Exception : dest */
            return (-1);
        }
        if (dest_size < 0)
        {
            /* Error -- dest_size < 0 */
            return (-1);
        }
    }
    if (src == NULL)
    {
        /* Error -- Null Pointer Exception : src */
        return (-1);
    }
    if (src_size < 1)
    {
        /* Error -- src_size < 1 */
        return (-1);
    }
    
    countNeedsWords = 0;
    for (cursor = 0; cursor < src_size;)
    {
        /* src より3バイトのデータを読み出し */
        nReadDataSize = (nMaxReadSize < (src_size - cursor))?(nMaxReadSize)
                :(src_size - cursor);
        memcpy(buffer, (src + cursor), nReadDataSize);
        memset(buffer + nReadDataSize, 0, sizeof(buffer) - nReadDataSize);
        
        /* data size の調べる */
        codeType = getCodeType(buffer, currentMode);
        switch (codeType)
        {
        case CODE_TYPE_OLD_JIS:         sizeBytes = 3;  break;
        case CODE_TYPE_NEW_JIS:         sizeBytes = 3;  break;
        case CODE_TYPE_ASCII:           sizeBytes = 3;  break;
        case CODE_TYPE_HANKAKU_KANA:    sizeBytes = 3;  break;
        case CODE_TYPE_RATEN:           sizeBytes = 3;  break;
        case CODE_TYPE_BYTE_SIZE1:      sizeBytes = 1;  break;
        case CODE_TYPE_BYTE_SIZE2:      sizeBytes = 2;  break;
        default:                        sizeBytes = 1;  break;
        }
        
        /*
         * dest_size をチェック
         */
        if (dest_size && (dest_size < (countNeedsWords + 1)))
        {
            /* Error : memory is not enough for dest */
            return countNeedsWords;
        }
        
        /* sizeBytes毎に処理を分岐 */
        if (dest_size)
        {
            unicode = DUMMY_CODE;
            switch (codeType)
            {
            case CODE_TYPE_OLD_JIS:
                currentMode = CODE_TYPE_OLD_JIS;
                break;
            case CODE_TYPE_NEW_JIS:
                currentMode = CODE_TYPE_NEW_JIS;
                break;
            case CODE_TYPE_ASCII:
                currentMode = CODE_TYPE_ASCII;
                break;
            case CODE_TYPE_HANKAKU_KANA:
                currentMode = CODE_TYPE_HANKAKU_KANA;
                break;
            case CODE_TYPE_RATEN:
                currentMode = CODE_TYPE_RATEN;
                break;
            case CODE_TYPE_BYTE_SIZE1:
                switch (currentMode)
                {
                case CODE_TYPE_ASCII:
                    unicode = ((wchar_t)buffer[0]) & 0x007f;
                    break;
                case CODE_TYPE_HANKAKU_KANA:
                    iso2022jpCode = ((wchar_t)buffer[0]) & 0x007f;
                    if (iso2022jpCode <= 0x20 || iso2022jpCode == 0x7f)
                    {
                        /*
                         * 規格外のデータだけど、対応。
                         */
                        unicode = iso2022jpCode;
                    }
                    else
                    {
                        unicode = iso2022jpCode -0x21 + 0xFF61;
                    }
                    break;
                case CODE_TYPE_RATEN:
                    iso2022jpCode = ((unsigned long)buffer[0]) & 0x007f;
                    if (iso2022jpCode <= 0x20 || iso2022jpCode == 0x7f)
                    {
                        /*
                         * 規格外のデータだけど、対応。
                         */
                        unicode = iso2022jpCode;
                    }
                    else
                    {
                        unicode = iso2022jp_to_utf16_table_1B284A[iso2022jpCode];
                    }
                    break;
                default:
                    break;
                }
                *dest = unicode;
                dest++;
                break;
            case CODE_TYPE_BYTE_SIZE2:
                iso2022jpCode = ((unsigned long)buffer[0] << 8) & 0x7f00;
                iso2022jpCode |= ((unsigned long)buffer[1]) & 0x007f;
                switch (currentMode)
                {
                case CODE_TYPE_OLD_JIS:
                    unicode = selectUnicodeFromOldJisTable(iso2022jpCode);
                    break;
                case CODE_TYPE_NEW_JIS:
                    unicode = selectUnicodeFromNewJisTable(iso2022jpCode);
                    break;
                default:
                    break;
                }
                *dest = unicode;
                dest++;
                break;
            default:
                break;
            }
        }
        switch (codeType)
        {
        case CODE_TYPE_BYTE_SIZE1:
        case CODE_TYPE_BYTE_SIZE2:
            countNeedsWords++;
        }
        cursor += sizeBytes;
    }
    
    return countNeedsWords;
}

/********************************************************************/
/**
 * printUsage() 関数。
 * 使用方法をコンソールに出力します。
 */
int printUsage()
{
    wprintf(L"iso2022jp_to_utf16.exe\n");
    wprintf(L"ISO-2022-JP から UTF-16 へ文字コードを変換します。\n");
    wprintf(L"使用法\n");
    wprintf(L"> iso2022jp_to_utf16 -i 入力ファイル -o 出力ファイル[Enter]\n");
    
    return 0;
}

/********************************************************************/
/**
 * 新JISテーブルよりUnicodeをセレクトします。
 * 
 * @param[in]   iso2022jpCode   ISO-2022-JPコード
 * @return      Unicode。対応するコードが見つからない場合は DUMMY_CODE を戻す。
 */
wchar_t selectUnicodeFromNewJisTable(unsigned long iso2022jpCode)
{
    unsigned char       firstByte       = 0;
    unsigned char       secondByte      = 0;
    unsigned long       firstIndex      = 0;
    unsigned long       secondIndex     = 0;
    wchar_t             unicode         = DUMMY_CODE;
    
    firstByte = (unsigned char)((iso2022jpCode >> 8) & 0x7f);
    secondByte = (unsigned char)((iso2022jpCode) & 0x7f);
    firstIndex = firstByte;
    /* 1:isEmpty 2:BitmapIndex 3:index */
    if (iso2022jp_to_utf16_index_table_1B2442[firstIndex].byType
            != BITYPE_BITMAP_INDEX)
    {
        return DUMMY_CODE;
    }
    secondIndex = iso2022jp_to_utf16_index_table_1B2442[firstIndex].dwIndex
            + secondByte;
    if (iso2022jp_to_utf16_index_table_1B2442[secondIndex].byType
            != BITYPE_INDEX)
    {
        return DUMMY_CODE;
    }
    unicode = iso2022jp_to_utf16_table_1B2442[
            iso2022jp_to_utf16_index_table_1B2442[secondIndex].dwIndex];
    
    if (unicode == 0x0000)
    {
        unicode = DUMMY_CODE;
    }
    
    return unicode;
}

/********************************************************************/
/**
 * 旧JISテーブルよりUnicodeをセレクトします。
 * 
 * @param[in]   iso2022jpCode   ISO-2022-JPコード
 * @return      Unicode。対応するコードが見つからない場合は DUMMY_CODE を戻す。
 */
wchar_t selectUnicodeFromOldJisTable(unsigned long iso2022jpCode)
{
    unsigned char       firstByte       = 0;
    unsigned char       secondByte      = 0;
    unsigned long       firstIndex      = 0;
    unsigned long       secondIndex     = 0;
    wchar_t             unicode         = DUMMY_CODE;
    
    firstByte = (unsigned char)((iso2022jpCode >> 8) & 0x7f);
    secondByte = (unsigned char)((iso2022jpCode) & 0x7f);
    firstIndex = firstByte;
    /* 1:isEmpty 2:BitmapIndex 3:index */
    if (iso2022jp_to_utf16_index_table_1B2440[firstIndex].byType
            != BITYPE_BITMAP_INDEX)
    {
        return DUMMY_CODE;
    }
    secondIndex = iso2022jp_to_utf16_index_table_1B2440[firstIndex].dwIndex
            + secondByte;
    if (iso2022jp_to_utf16_index_table_1B2440[secondIndex].byType
            != BITYPE_INDEX)
    {
        return DUMMY_CODE;
    }
    unicode = iso2022jp_to_utf16_table_1B2440[
            iso2022jp_to_utf16_index_table_1B2440[secondIndex].dwIndex];
    
    if (unicode == 0x0000)
    {
        unicode = DUMMY_CODE;
    }
    
    return unicode;
}

・2009-01-07

右サイドメニュー