JSONのユニコードエスケープ処理にICUを使用する


ユニコードエスケープの対応

JSON形式では、GETやPOSTでWebサーバーに要求を送ると日本語の文字列は下の例のようにユニコードエスケープ処理されて返ってきます。

Json文字列の一部
"Type":"A10","Code":"9876","Name":"\u3053\u3093\u306b\u3061\u306f"

上の例にある先頭の/u3053は日本語の「こ」で16ビットのコードポイントです。5文字をあわせて「こんにちは」になります。

コードポイントをC/C++で日本語にする場合、大変高度なCの知識が必要になりコード量が増加します。これを回避するため、ICUを使います。

ICUを使えば、Web時代でのWindowsアプリケーションのプログラミングが楽になると思います。

What is ICU?

International Components for Unicodeを略し、ICUと呼びます。ユニコードを扱うオープンソースライブラリーです。

C/C++、JAVAでこのライブラリーを利用してアプリケーションが作成できます。世界の大手IT企業がICUを利用してアプリケーションを作成しています。

これまでに広範に利用され続け、成熟された内容に進化し続けています。いまでも頻繁にアップデートされている。

ダウンロードと環境構築

PCの環境としてWindows10,Visual Studio 2017を前提にしています。

ICUのソースコードをホームページのダウンロードサイトからダウンロードします。
ICU4CがC用のソースです。
ICU4C Source Code Downloadの項目からZIP file for Windows platformsを選びます。
ファイル名はicu4c-62_1-src.zipですが、62_1はリリース番号です。
git-hubの場合、こちらダウンロードしてください。

ダウンロードしたら任意のフォルダーに解凍し、icuフォルダーがあるのを確認します。
そのなかにあるsourceフォルダーのallinoneというフォルダーを開き、Visual studioでソリューションファイル「allinone.sln」を開きます。開いたらリリースモードでビルドします。

ビルドが完了するとicuフォルダーの直下にlib、 binフォルダーがそれぞれ作成されます。
今度はicuフォルダーをCドライブ直下に移動させます(フォルダーの置き場所は任意の場所でOkです)。

pathを通すため、windowsの環境変数のpathの項目にc:\icu\binを追加します。

ICUを使用したいプロジェクトを作り、プロパティ→リンカー→入力→追加の依存に次のファイルを追加します。

追加の依存ファイル(コピペしてください)
icuuc.lib;icuin.lib

同じくライブラリーディレクトリにc:\icu\libとインクルードディレクトリにC:\icu\includeを追加します。

プロジェクトフォルダのソースファイル(cpp/cファイル)のある場所に以下のファイルをicuフォルダーからコピー&ペーストします。

以下のファイルをプロジェクトフォルダーにコピペする

icuin62.dll
icuuc62.dll
icudt62.dll

Coding with C

今回はCにてユニコードのコードポイントからCString型に変換します。

Coding_With_C
// Visual C++ の CLR、 CLR empty projectで作成。
// Visual StudioからMFC関連のAPIを導入してください。

#include <atlstr.h>
#include <unicode\unistr.h> // ICUのヘッダーを追加する。

int main() {

// WEBサーバーからのJSON形式のレスポンスの一部。(constにはできない文字列。)
    char src[] = "\"Type\":\"A10\",\"Code\":\"9876\",\"Name\":\"\\u3053\\u3093\\u306b\\u3061\\u306f\"";
    char separator[] = "\\u"; //区切り文字をセット。
    char dq[] = "\"";
    char targetWord[] = "Name\":\""; // ターゲットとなる項目の文字列を選ぶ。
    char* pSrc = NULL;
    char* gotWord = NULL;
    char* answer = NULL;
    char* token = NULL;
    char temp[256];
    icu::UnicodeString uniStr;
    CString strCstr;
    HWND hWnd = NULL;

    pSrc = strstr(src, targetWord); // ターゲットの文字列の先頭にポインターを移す。
    pSrc += strlen(targetWord); // ターゲット項目の文字列の数だけポインターをずらす。
    gotWord = strtok_s(pSrc, dq, &token);// ポインター位置からダブルクオーテーションまでの文字を抜き出す。

    answer = strtok_s(gotWord, separator, &token);//区切り文字を「\\u」として、文字列を抽出する。
    while (answer != NULL) {

        strcpy_s(temp, sizeof(temp), "0x");
        strcat_s(temp, sizeof(temp), answer); //文字列を0xXXXXに変換する。
        uniStr = strtol(temp, NULL, 16); // ICUのUnicodeStringに16進数へ変換した文字を入れる。
        strCstr.AppendFormat(_T("%s"), uniStr.getTerminatedBuffer()); // CString形式に変換して文字を追加する。

        answer = strtok_s(NULL, separator, &token);//次の文字列を探す。
    }

    MessageBox(hWnd, strCstr, _T("Message"), MB_OK | MB_ICONINFORMATION);//メッセージを表示する。

    return 0;
}