nkfとは、市川至、森和彦氏らによって開発された、漢字コード変換フィルタです。
また、海人氏により32ビット版に移植されました(もちろん市川氏らの32ビット版もある)。EUC,S-JIS,JISコード変換が非常に簡単に実現できます。ここでは、海人氏の32ビット版DLLを使った、簡単なプログラムを
作ってみます。
まずは、ベクター等よりNKF32を入手し適当なディレクトリにインストールします。ここでは、C:\nkfにインストールしてたものとします。
この中に、nkf32.h, hkf32.dll, nkf32.lib, nkf32b.lib, nkf32.docが入っています。
dllは、システムのフォルダなどパスの通っているディレクトリにコピーしておくと便利です。
ここにある、ヘッダファイルと、ライブラリファイルをそのまま使うためには、C++ではなく、Cでプログラムを書く必要があります。
さて、VC++では次のような設定をしておきます。
プロジェクトにnkf32.libを参加させます。(注:nkf32b.libはC++Builder用です) ヘッダファイルをプロジェクトのフォルダにコピーしてもいいのですが、ここでは、 ソリューション・エクスプローラを右クリックしてプロパティを選択し、C/C++ の「全般」「追加のインクルードディレクトリ」にc:\nkfを追加しておきます。
今回は、簡単のためS-JISで入力された、テキストをEUCに変換して出力する簡単な
エディタを作ってみます。
// nkf01.rcの一部
/////////////////////////////////////////////////////////////////////////////
//
// Menu
//
MYMENU MENU 
BEGIN
    POPUP "ファイル(&F)"
    BEGIN
        MENUITEM "終了(&X)...",                 IDM_END
        MENUITEM "名前を付けてEUCに変換して保存(&A)...", IDM_SAVEAS
    END
END
普通のメニューのリソース・スクリプトです。
// nkf01.c #define ID_EDIT 100 #include <windows.h> #include "resource.h" #include "nkf32.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); BOOL MySaveAs(HWND, HWND); char szClassName[] = "nkf01"; //ウィンドウクラス HINSTANCE hInst;ソース・ファイルの拡張子は「C」にしておきます。nkf32.hをインクルードしておきます。
int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,
                   LPSTR lpsCmdLine, int nCmdShow)
{
    MSG msg;
    BOOL bRet;
    
    hInst = hCurInst;
    if (!InitApp(hCurInst))
        return FALSE;
    if (!InitInstance(hCurInst, nCmdShow)) 
        return FALSE;
    while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
        if (bRet == -1) {
            break;
        } else {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (int)msg.wParam;
}
//ウィンドウ・クラスの登録
ATOM InitApp(HINSTANCE hInst)
{
    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;    //プロシージャ名
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;//インスタンス
    wc.hIcon = (HICON)LoadImage(NULL,
        MAKEINTRESOURCE(IDI_APPLICATION),
        IMAGE_ICON,
        0,
        0,
        LR_DEFAULTSIZE | LR_SHARED);
    wc.hCursor = (HCURSOR)LoadImage(NULL,
        MAKEINTRESOURCE(IDC_ARROW),
        IMAGE_CURSOR,
        0,
        0,
        LR_DEFAULTSIZE | LR_SHARED);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = "MYMENU";    //メニュー名
    wc.lpszClassName = (LPCSTR)szClassName;
    wc.hIconSm = (HICON)LoadImage(NULL,
        MAKEINTRESOURCE(IDI_APPLICATION),
        IMAGE_ICON,
        0,
        0,
        LR_DEFAULTSIZE | LR_SHARED);
    return (RegisterClassEx(&wc));
}
//ウィンドウの生成
BOOL InitInstance(HINSTANCE hInst, int nCmdShow)
{
    HWND hWnd;
    hWnd = CreateWindow(szClassName,
            "猫でもわかるnkf", //タイトルバーにこの名前が表示されます
            WS_OVERLAPPEDWINDOW, //ウィンドウの種類
            CW_USEDEFAULT,    //X座標
            CW_USEDEFAULT,    //Y座標
            CW_USEDEFAULT,    //幅
            CW_USEDEFAULT,    //高さ
            NULL, //親ウィンドウのハンドル、親を作るときはNULL
            NULL, //メニューハンドル、クラスメニューを使うときはNULL
            hInst, //インスタンスハンドル
            NULL);
    if (!hWnd)
        return FALSE;
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    return TRUE;
}
いつもと同じです。現在のインスタンスハンドルをグローバル変数にコピーしておきました。
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
    int id;
    static HWND hEdit;
    switch (msg) {
        case WM_CREATE:
            hEdit = CreateWindow("EDIT", "", 
                WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |
                ES_MULTILINE | ES_AUTOHSCROLL |    ES_AUTOVSCROLL | 
                ES_WANTRETURN, 
                0, 0, 0, 0, hWnd, (HMENU)ID_EDIT, hInst, NULL);
            break;
        case WM_SIZE:
            MoveWindow(hEdit, 0, 0, LOWORD(lp), HIWORD(lp), TRUE);
            break;
        case WM_SETFOCUS:
            SetFocus(hEdit);
            break;
        case WM_COMMAND:
            switch (LOWORD(wp)) {
                case IDM_END:
                    SendMessage(hWnd, WM_CLOSE, 0, 0);
                    break;
                case IDM_SAVEAS:
                    MySaveAs(hWnd, hEdit);
                    break;
            }
            break;
        case WM_CLOSE:
            id = MessageBox(hWnd, "終了してもよろしいですか",
                    "終了確認", MB_OKCANCEL);
            if (id == IDOK) {
                DestroyWindow(hEdit);
                DestroyWindow(hWnd);
            }
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return (DefWindowProc(hWnd, msg, wp, lp));
    }
    return 0;
}
メイン・ウィンドウのプロシージャです。WM_CREATEメッセージが来たら、エディットコントロールを作っておきます。
WM_SIZEメッセージが来たら、エディットコントロールの大きさを調整して、クライアント 領域と同じ大きさにしておきます。
WM_SETFOCUSメッセージが来たら、SetFocus関数でエディットコントロールに、キーボード・フォーカスを設定します。このメッセージは、ウィンドウがキーボード・フォーカス を取得したら送られてきます。
LRESULT CALLBACK WindowProc( HWND hwnd, // ウィンドウハンドル UINT uMsg, // WM_SETFOCUS WPARAM wParam, // ウィンドウハンドル LPARAM lParam // 使用しない );メニューから、IDM_SAVEASが選択されたら、自作関数MySaveAsを呼びます。
BOOL MySaveAs(HWND hWnd, HWND hEdit)
{
    HLOCAL hMem;
    HANDLE hHeap, hFile;
    char *lpszBuf, *lpszOut; 
    static char szFile[MAX_PATH] = "", szFileTitle[MAX_PATH] = "";
    OPENFILENAME ofn;
    int nLen, nLenE, i;
    DWORD dwWritten;
    hMem = (HLOCAL)SendMessage(hEdit, EM_GETHANDLE, 0, 0);
    lpszBuf = (char *)LocalLock(hMem);
    LocalUnlock(hMem);
    memset(&ofn, 0, sizeof(OPENFILENAME));
    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.hwndOwner = hWnd;
    ofn.lpstrFile = szFile;
    ofn.nMaxFile = MAX_PATH;
    ofn.lpstrFileTitle = szFileTitle;
    ofn.nMaxFileTitle = MAX_PATH;
    ofn.Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY;
    ofn.lpstrDefExt = "txt";
    ofn.lpstrFilter = "text(*.txt)\0*.txt\0All files(*.*)\0*.*\0\0";
    if (GetSaveFileName(&ofn) == 0)
        return FALSE;
    nLen = (int)strlen(lpszBuf);
    hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS, (nLen + 256) * 3, 0);
    lpszOut = (char *)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, (nLen + 256) * 3);
    SetNkfOption("-Se");
    NkfConvert(lpszOut, lpszBuf);
    hFile = CreateFile(szFile, GENERIC_WRITE,
        0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        MessageBox(hWnd, "CreateFile関数失敗", "Error", MB_OK);
        HeapDestroy(hHeap);
        return FALSE;
    }
    nLenE = (int)strlen(lpszOut);
    for (i = 0; i < nLenE; i++) {
        if (lpszOut[i] == 0x0d)    {
            continue;    
        } else {
            WriteFile(hFile, &lpszOut[i], 1, &dwWritten, NULL);
        }
        if (dwWritten == 0) {
            MessageBox(hWnd, "書き込みエラーです", "Error", MB_OK);
        }
    }
    SetEndOfFile(hFile);
    CloseHandle(hFile);
    HeapDestroy(hHeap);
    return TRUE;
}
エディットコントロールの文字列を取得して、これをECUに変換して、ファイルに保存する
関数です。エディットコントロールから文字列を取得するにはGetWindowText関数でもよいのですが、 ここでは、EM_GETHANDLEメッセージを送って、エディットコントロールのメモリハンドルを取得しています。
SendMessage( (HWND) hWnd, // ウィンドウハンドル EM_GETHANDLE, (WPARAM) wParam, // 使用しない(0にしておく) (LPARAM) lParam // 使用しない(0にしておく) );成功すれば、複数行エディットコントロールが内容を保持しているメモリのハンドルが 返されます。失敗した時や、単一行エディットコントロールに送った場合は0が返されます。
メモリオブジェクトのハンドルが返されたら、LocalLock関数で、ポインタを取得できます。
LPVOID LocalLock( HLOCAL hMem // ローカルメモリオブジェクトのハンドル );関数が失敗した時はNULLが返されます。
LocalLockしたものはLocalUnlockしなくてはいけません。
BOOL LocalUnlock( HLOCAL hMem // ローカルメモリオブジェクトのハンドル );さて、次にGetSaveFileName関数で、保存するファイル名をユーザーに 入力させます。
次に、EUC変換のための前準備をしておきます。 まずは、strlen関数でエディットコントロールに入力されている、バイト数を求めておきます。SJISからEUCに変換した場合、サイズはほとんど変わりません。改行があると、そこで 1バイト少なくなります。
ここでは、(nLen + 1) バイト程度のメモリを出力用に確保しておけば充分でしょう。 今回は、HeapAlloc関数で動的にメモリを確保してみます。この関数を使う前に、HeapCreate関数で、使用可能なヒープオブジェクトを作成しておくことが必要です。 ヒープオブジェクトを作成した後に、HeapAlloc関数でメモリを割り当てます。
メモリを解放するにはHeapFree関数を使います。また、HeapDestroy関数で、ヒープオブジェクトを破棄することもできます。この場合、HeapFree関数を使う必要はありません。
HANDLE HeapCreate( DWORD flOptions, // ヒープ割り当て方法 SIZE_T dwInitialSize, // 初期のヒープサイズ SIZE_T dwMaximumSize // 最大ヒープサイズ );flOptionsには、作成したいヒープオブジェクトの属性を指定します。
HEAP_GENERATE_EXCEPTIONSは、関数が失敗した時に例外を発生させます。
HEAP_NO_SERIALIZEは、メモリの割り当てや解放を行う時、相互排他を行いません。
(普通は、指定しません)
dwInitialSizeには、ヒープの初期サイズを指定します。
dwMaximumSizeには、ヒープの最大サイズを指定します。0を指定すると拡張可能となります。
関数が成功するとヒープハンドルが返されます。
LPVOID HeapAlloc( HANDLE hHeap, // ヒープブロックのハンドル DWORD dwFlags, // ヒープの割り当て方法 SIZE_T dwBytes // 割り当てたいバイト数 );hHeapには、ヒープハンドルを指定します。
dwFlagsには、割り当て方法のオプションを指定します。
HEAP_GENERATE_EXCEPTIONSは、関数が失敗した時に例外を発生させます。
HEAP_NO_SERIALIZEは、この関数がヒープにアクセスしている時相互排他を行いません。
HEAP_ZERO_MEMORYは、割り当てたメモリを0で初期化します。
dwBytesには、確保したいバイト数を指定します。HeapCreate関数で指定したヒープが 拡張不能の時は、0x7FFF8バイト未満の数値を指定しなくはなりません。
関数が成功すると、メモリブロックへのポインタが返されます。
メモリの準備ができたら、書き込まれた内容をEUCに変換します。
これは、至って簡単です。
SetNkfOption関数で変換オプションを指定します。
int CALLBACK SetNkfOption(LPCSTR optStr);optStrに変換オプション文字列を指定します。詳しくは添付のドキュメントを読んでください。簡単に覚えるには、入力側は大文字、出力側は小文字を指定します。
たとえば、入力側がS-JISの場合は、"-S"、出力側がEUCの場合は"-e"となります。 これをまとめて"-Se"とすることもできます。(「小さくなって出ていく」と覚えると忘れません。)また、入力側を指定しないと自動的に 判断してくれますが、100%正しいとは言い切れないので、入力側も指定するのが 安全だと思います。
さて、オプションを設定したら、実際に変換をします。
void CALLBACK NkfConvert(LPSTR outStr, LPCSTR inStr);outStrには、出力を受けるバッファを指定します。
ここでは、ファイルに1バイトずつ書き込み、"\r"が出てきたら、書き込まない方式を とっています。
nkfの使い方は、非常に簡単です。これを自力でやるのはかなり、面倒くさいので 非常に重宝します。この他に、BASE64や、半角全角の変換などもできます。研究して みてください。
Update 05/Oct/2004 By Y.Kumei