山本ワールド
Windowsプログラミング
アルゴリズム Vitual C++ 2008/2013によるWin32/Win64 APIレベルのプログラム 基礎 Vitual C++ 2008/2013によるAPIレベルのプログラム(32/64bit) Wix3でインストーラーを作る Visual C++ 2008 Standard Editonによるフォームアプリケーションのプログラム(32/64bit) Vitual C++ 2008 Standard EditonによるAPIレベルのプログラム(32/64bit) Windows 7対応 Visual C++ 2008 ExpressによるAPIレベルのプログラム Visual C++ 2005 ExpressによるAPIレベルのプログラム Visual C++ Versiosn 5 BORLAND C++ Windowsプログラム全般 Excel VBA その他UNICODEのサポート
概要
Windows NT/2000/XP/Vistaは内部でUNICODEを使用している。
Windows 9x系の日本語版ではShift-JISは使っている。
Shift-JISとは、ANSIコードの使っていないコード領域に2バイトのJIS漢字コードを割り当てたものである。MS-DOSの時代から使われている。英数字等は1バイトで、漢字の場合2バイトとなる。Visual C++では普通にchar型である。コードページ(JISやShift-JISやECUなどを指定するためのコード)が違えば文字化けの原因になったりする。また、文字数を数えるのに1文字1文字、先頭から漢字が英数字を判断して数えなければならないので煩雑であるなどの問題点がある。
UNICODEでは、全世界の文字を単一の文字コードで取り込むという壮大?な理想をもとに作成された文字コードである。Windows NT系(NT/2000/XP/Vista/7/8/8.1/10/Server)でサポートされているのは、UTF16(LE)である。
Visual C++ 2005以降のプロジェクト作成時はUNICODEがディフォルトとなっている。UNICODEを使わないようにするためには、プロジェクトのオプションでマルチバイト等を選択する。プロジェクトでUNICODEが指定されている場合は、UNICODEマクロが定義される。
Windows9x系では、APIがMessageBoxぐらいしかUNICODEをサポートしていないため、Shift-JISで作成すべきであろう。TCHAR.Hで用意されているマクロを使えばUNICODE対応版とShift-JIS対応版を1つのソースから作成するのが楽になる。
テスト環境
コンパイラ
Visual C++ 2008 Standard 32/64bitVisual C++ 2013 Express 32/64bit
実行環境
Windows 8.1 Enterprise 64bitWindows 7 EnterPrise Service Pack 1 64bit
Windows Vista Ultimate Service Pack 2 32bit
Windows XP Professional Service Pack 3 32bit
UNICODEによる文字列および文字定数の記述方法
文字列
Shift-JISの場合
普通にchar* buf="漢字"; // char* buf = { 0x8a , 0xbf , 0x83 , 0x9a , 0 };
char* buf="0123"; // char* buf = { 0x30 , 0x31 , 0x32 , 0x33 , 0 };
UNICODEの場合
Lを付けるWCHAR* buf = L"漢字"; // WCHAR* buf={ 0x6f22 , 0x5b57 , 0x0000 };
// byte列で表現すると 0x22 , 0x6f , 0x57 , 0x5b , 0x00 , 0x00
WCHAR* buf = L"0123"; // WCHAR* buf={ 0x0030 , 0x0031 , 0x0032 , 0x0033 , 0 };
// byte列で表現すると 0x30 , 0x00 , 0x31 , 0x00 , 0x32 , 0x00 , 0x33 , 0x00 , 0x00 ,0x00
Shift-JISとUNICODEをサポート
TCHAR* buf=_TEXT( "漢字" );
WCHARはshort intとして定義されている。TCHAR型は、ヘッダーファイルtchar.hで定義されている。UNICODEマクロが定義されている場合はWCHAR、定義されていない場合はcharに展開される。
_TEXT("x")マクロは、UNICODEマクロが定義されている場合はL"x"、定義されていない場合は"x"に展開される。
文字定数
文字列と同じように _T( 'x' ) マクロを使う
UNICODEと他のエンコーディングとの変換
UNICODE文字列を他のエンコーディングに変換する
WideCharToMultiByte APIを使用するとUNICODE文字列を他のエンコーディングに変換できます。cchMultiByteに0を代入してAPIを呼び出すと変換後のバイト数が返されますので動的にバッファを確保する場合に使用することができます。
プロトタイプ
int WideCharToMultiByte(UINT CodePage , DWORD dwFlags , LPCWSTR lpWideCharStr , int cchWideChar , LPSTR lpMultiByteStr , int cchMultiByte , LPCSTR lpDefaultChar , LPBOOL lpUsedDefaultChar);
CodePage:コードページdwFlags:処理速度とマッピング方法を決定するフラグ
lpWideCharStr:ワイド文字列のアドレス
cchWideChar:ワイド文字列の文字数
lpMultiByteStr:新しい文字列を受け取るバッファのアドレス
cchMultiByte:新しい文字列を受け取るバッファのサイズ
lpDefaultChar:マップできない文字の既定値のアドレス
lpUsedDefaultChar:既定の文字を使ったときにセットするフラグのアドレス コードページ
定数 | 意味 |
---|---|
CP_ACP | ANSI コードページ |
CP_MACCP | Macintosh コードページ |
CP_OEMCP | OEM コードページ |
CP_SYMBOL | シンボルコードページ(42) |
CP_THREAD_ACP | 現在のスレッドの ANSI コードページ |
CP_UTF7 | UTF-7 を使った変換 |
CP_UTF8 | UTF-8 を使った変換 |
932 | SHIFT-JIS |
51932 | EUC-JP |
50220 | JIS.ISO-2022-JP |
使用例
以下のソースは、UNICODE文字列へのポインタtをShift-JISに変換し配列sjisに格納します。WCHAR* t=L"テスト漢字123";
char sjis[128];
WideCharToMultiByte(932, 0, t, -1, sjis, sizeof(sjis), NULL, NULL);
他のエンコーディングをUNICODE文字列に変換する
MultiByteToWideChar APIを使用すると他のエンコーディングをUNICODEに変換できます。cchWideCharに0を代入してAPIを呼び出すと変換後の文字数が返されますので動的にバッファを確保する場合に使用することができます。
プロトタイプ
int MultiByteToWideChar(UINT CodePage , DWORD dwFlags , LPCSTR lpMultiByteStr , int cchMultiByte, LPWSTR lpWideCharStr , int cchWideChar);
CodePage:コードページ(WideCharToMultiByte参照)dwFlags:文字の種類を指定するフラグ
lpMultiByteStr:マップ元文字列のアドレス
cchMultiByte:マップ元文字列のバイト数
lpWideCharStr:マップ先ワイド文字列を入れるバッファのアドレス
cchWideChar:バッファのサイズ
使用例
以下のソースは、Shift-JIS文字列へのポインタstrをUNICODE文字列に変換し配列utfに格納します。char str="テスト漢字123";
WCHAR utf[16];
MultiByteToWideChar(932, 0, str, -1, utf, sizeof(utf) / sizeof(TCHAR));
UNICODEによるファイル入出力
書き込み
何も指定せずに_tfopen_sを使用すると、エンコーディングがANSIになっているので、Shift-JISで書き込まれる。_tfopen_sの第3引数でエンコーディングを指定するとUNICODEで保存することができます。
エンコーディングにはUNICODEの他、UTF-8またはUTF-16LEを指定することができます。
UNICODE文字列がShift-JISで書き込まれる例
エンコーディングがANSIになっているので、Shift-JISで書き込まれます。UNICODE・マルチバイトどちらでコンパイルしてもエンコーディングに依存するためShift-JISで書き込まれます。
#include <stdio.h>
#include <tchar.h>
#include <locale.h>
void _tmain(void){
// UNICODE文字を標準出力に正しく表示させるためにロケールを設定
_tsetlocale(LC_ALL, _TEXT(""));
FILE* fp;
if(_tfopen_s(&fp, _TEXT("test.txt"), _TEXT("w"))){
_tprintf(_TEXT("ファイルが開けませんでした。"));
return;
}
_ftprintf(fp, _TEXT("テスト漢字123\n"));
fclose(fp);
}
書き込まれたファイルを16進ダンプした結果(ファイルサイズ15バイト)
0000 83 65 83 58 83 67 8a bf 8e 9a 31 32 33 0d 0a テスト漢字123\n
UNICODE文字列がUNICODEで書き込まれる例
UNICODEでコンパイルした場合、_tfopen_sでccs=UNICODEを指定しているのでファイルにUNICODEで書き込まれます。マルチバイトでコンパイルした場合、ANSI版の関数が使用されます。_ftprintfのANSI版でfprintfが使用されますが、UNICODEに変換して書き込む機能をもたないので実行時にエラーが発生します。 マルチバイトで正常にUNICODEで保存するには、MultiByteToWideChar APIでUNICODEに変換してからファイルに書き込みます。
UNICODEでコンパイルした場合とマルチバイトでコンパイルした場合のコードを切り替えるためにプロジェクトがUNICODEの時定義されるUNICODEマクロと#ifを使用します。
UNICODEとマルチバイト両方のコンパイルをサポートする必要がない場合はもっとシンプルに記述できます。
// UNICODE文字列をファイルにUNICODE文字列として書き込みプログラム
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#include <locale.h>
void _tmain(void){
// UNICODE文字を標準出力に正しく表示させるためにロケールを設定
_tsetlocale(LC_ALL, _TEXT(""));
FILE* fp;
#ifdef UNICODE
if (_tfopen_s(&fp, _TEXT("test.txt"), _TEXT("w,ccs=UNICODE"))){
#else
if (_tfopen_s(&fp, _TEXT("test.txt"), _TEXT("w"))){
#endif
_tprintf(_TEXT("ファイルが開けませんでした。"));
return;
}
TCHAR* str=_TEXT("テスト漢字123\n");
#ifdef UNICODE
_ftprintf(fp, str);
#else
WCHAR utf[16];
MultiByteToWideChar(932, 0, str, -1, utf, sizeof(utf) / sizeof(TCHAR));
fwprintf(fp,utf);
#endif
fclose(fp);
}
書き込まれたファイルを16進ダンプした結果(ファイルサイズ22バイト)
0000 ff fe c6 30 b9 30 c8 30 22 6f 57 5b 31 00 32 00 テスト漢字123\n 0010 33 00 0d 00 0a 00ファイルの先頭のff feは、UNICODEテキストを表すBOMです。
読み込み
何も指定せずに_tfopen_sを使用すると、コードページがANSIになっているので、UNICODEファイルは正常に読み込まれない。_tfopen_sの第3引数でエンコーディングを指定するとUNICODEファイルとして認識されUNICODEとして読み込むことができます。
ただし、正常に読み込めるのはUNICODEでコンパイルした場合で、マルチバイトの場合、正常に読み込まれません。
マルチバイトの場合は、fgetwsでUNICODEとして読み込みWideCharToMultiByte APIでUNICODE文字列をShift-JISに変換します。
// UNICODEファイルをUNICODE文字列として読み込むプログラム
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#include <locale.h>
void _tmain(void){
// UNICODE文字を標準出力に正しく表示させるためにロケールを設定
_tsetlocale(LC_ALL, _TEXT(""));
FILE* fp;
if (_tfopen_s(&fp, _TEXT("test.txt"), _TEXT("r,ccs=UNICODE"))){
_tprintf(_TEXT("ファイルが開けませんでした。"));
return;
}
#ifdef UNICODE
TCHAR buf[32];
_fgetts(buf, sizeof(buf) / sizeof(TCHAR), fp);
_tprintf(_TEXT("%s"),buf);
#else
WCHAR buf[32];
char sjis[32];
fgetws(buf, sizeof(buf) / sizeof(WCHAR), fp);
WideCharToMultiByte(932, 0, buf, -1, sjis, sizeof(sjis), NULL, NULL);
printf("%s", sjis);
#endif
fclose(fp);
}
標準出力へ書き込む
コンソールのコードページはANSIとなっているため、UNICODEの出力は正常にできない。コードページはコマンドプロンプトでchcpコマンドを実行することで確認できる。
C:¥>chcp 現在のコード ページ: 932
正常に動作しない例
ソースコード
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
void _tmain(void){
_tprintf(_TEXT("_tprintf関数:漢字出力テスト\n"));
}
実行結果
_tprintf
ロケールを指定
_tsetlocale関数によりロケールを設定するとUNICODE文字列が正常に表示できる。ソースコード
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <locale.h>
void _tmain(void){
// UNICODE文字を標準出力に正しく表示させるためにロケールを設定
_tsetlocale(LC_ALL, _TEXT(""));
_tprintf(_TEXT("_tprintf関数:漢字出力テスト\n"));
}
実行結果
_tprintf関数:漢字出力テスト
Shift-JISに変換して出力
ソースコード
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
//#include <locale.h>
void _tmain(void){
TCHAR* t = _TEXT("UNICODEをマルチバイト文字列に変換してからprintf:漢字出力テスト\n");
#ifdef UNICODE
char sjis[128];
WideCharToMultiByte(932, 0, t, -1, sjis, sizeof(sjis), NULL, NULL);
printf("%s", sjis);
#else
printf("%s", t);
#endif
}
実行結果
UNICODEをマルチバイト文字列に変換してからprintf:漢字出力テスト
UNICODEおよびマルチバイト両方で使える標準関数
マルチバイト(_MBCSが定義されているとき) | UNICODE/マルチバイト両方で使える関数 | UNICODEが定義されているとき |
main | _tmain | wmain |
fopen | _tfopen | _wfopen |
printf | _tprintf | wprintf |
sprintf | _stprintf | _swprintf |
fputc | _fputtc | fputwc |
fputs | _fputts | fputws |
fgetchar | _fgettchar | fgetwchar |
fgets | _fgetts | fgetws |
strcmp | _tcscmp | wcscmp |
strcpy | _tcscpy | wcscpy |
atoi | _ttoi | _wtoi |
strtod(atofの代用) | _tcstod | wcstod |
isdigit | _istdigit | iswdigit |
UNICODEとマルチバイトでソースの記述を使い分ける方法
#ifdef UNICODE
// UNICODEの処理を記述します。
#else
// マルチバイトの処理を記述します。
#endif