2017年8月
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    
無料ブログはココログ

« 株:現物売り約定(三菱UFJファイナンシャル・グループ、カプコン) | トップページ | 今週届いた本 »

2017年6月11日 (日)

文字コードについて(主にWindows)

お仕事で、文字コードに苦しんだのでちょっと調べてみた。
ファイル出力されたUNICODE(UTF-8)、Windowsアプリ内でBSTRやら_bstr_tやらの扱いが混乱してしまった。あと、WideChar、マルチバイト文字、2バイト文字とか。
文字コードで一般的に頭に思い浮かべるのはASCII、SHIFT-JIS、UTF-8。UNICODEも。UNICODEとUTF-8、UTF-16とかの違いは?EUC-JPってのもある。

つらつらと書きだしてみる。。

  • UNICODEとUTF-8について。
    UNICODEは、符号化文字集合や文字符号化方式を定めたもの。
  • 符号化文字集合は、文字に対する数値が割り振られたもの(論理コードとでもいうべきか)。文字コード、コードセット、キャラセット、CES。UNICODEは、U+0000~U+10ffffとか表記される。”犬”は、UNICODEでU+72ac
  • 文字符号化方式は、符号化文字集合をバイト列に扱う仕様、方法を記したもの(物理コードという感じ?)。エンコーディング、CEF。UTF-8は、UNICODEの符号化文字集合の一つで1~4バイトで表現される。”犬”をUTF-8でバイト列にすると0xe78aac。UTF-16だと0x72ac。SHIFT-JISだと0x8ca2。
    ASCII文字類はUTF-8・UTF-16だと0x00を加えて2バイトにする。
  • BSTRは、内部でWideCharで扱われる。正確に言うと、OLECHAR *であり、OLECHARはWIN32であるとき+OLE2ANSIではないときにWCHARとなる。それ以外の時はchar型となる。
  • _bstr_tは、BSTRのスマートポインタ、ヘルパークラス。デバッグで見るとメンバ変数を見ると、m_wstrメンバ変数(BSTR)とm_strメンバ変数(mutable char*)、m_RefCount(unsigned long:スマートポインタのカウンタ)がある。
    _bstr_tの変数を作ると、m_wstrメンバ変数にUTF-16(WideCharな 2バイト文字)で格納される。(const char*)でキャストするとm_strメンバ変数に新たに領域を確保してからロケールに従った変換結果(日本ならSHIFT-JISのマルチバイト)を格納する。変数の参照が終わると領域を解放する。
    ちなみに、DLL内の関数に_bstr_t変数を渡して(const char*)でキャストすると、_bstr_t変数のスコープ外にでたときに領域解放できない。EXEとDLLのメモリ空間が異なるため。BSTRで渡してDLL内部で変換するべき。
  • UNICODE(WideChar)をUTF-8やSHIFT-JIS(マルチバイト)に変換するには、WindowsAPIではWideCharToMultiByte()を使う。その逆はMultiByteToWideChar()を使う。UTF-8とSHIFT-JISの変換は、いったんUNICODE(WideChar)に変換してから、変換したい文字コード(マルチバイト)にする。
  • BSTRからロケールに合わせた文字コードにするにはOLE2T、W2Aとか使えるかも。MSDN「ATLとMFCの文字列変換マクロ」を参照のこと。

テキストエディタで保存した、SHIFT-JIS、UTF-8、UTF-16、UTF-16(BigEndian)をVisualStudioでバイナリとして開いてみると以下のようになる。UTF-8,16,16(BE)の先頭3バイトはBOMが付いている。

Photo

試してみたプログラム

// TestBSTR.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//
#pragma comment(lib,"user32.lib")
#pragma comment(lib,"comsuppw.lib")	// WideChar (link comsuppw.lib)
//#pragma comment(lib,"comsupp.lib")	// Not WideChar (link comsupp.lib)

#include "stdafx.h"
#include <windows.h>
#include <comutil.h>
#include <tchar.h>
#include <mbstring.h>

void dumpBin(LPCSTR type, LPCSTR data, size_t len) {
	printf("%s (address=0x%08x): \n", type, (unsigned int)data);
	for (size_t idx = 0; len > idx; idx++) {
		printf("\t");
		for (size_t cnt = 0; (len > idx) && (16 > cnt); cnt++, idx++) {
			printf("%02x ", (unsigned char)data[idx] );
		}
		printf("\n");
	}
}

// WideCharからMultiByteに変換
//関数内で領域を確保するので、使用後は開放すること
int convertWideToMulti(const wchar_t *pwsData, UINT encoding, LPSTR *pstrRslt)
{
	size_t len = wcslen(pwsData);
	int iW2MLen1 = WideCharToMultiByte(encoding, 0, pwsData, len, NULL, 0, NULL, NULL);
	if (iW2MLen1 == 0) {
		// 失敗
		return -1;
	}
	*pstrRslt = (LPSTR)malloc(iW2MLen1 + 1);
	if (*pstrRslt == NULL) {
		// 失敗
		return -1;
	}
	ZeroMemory(*pstrRslt, iW2MLen1 + 1);
	int iW2MLen2 = WideCharToMultiByte(encoding, 0, pwsData, len, (LPSTR)*pstrRslt, iW2MLen1, NULL, NULL);
	if (iW2MLen2 == 0) {
		// 失敗
		free(*pstrRslt);
		return -1;
	}
	return iW2MLen2;
}

// MultiByteからWideCharに変換
//関数内で領域を確保するので、使用後は開放すること
int convertMultiToWide(LPCSTR pcstrData, UINT encoding, LPWSTR *ppwsRslt )
{
	size_t len = strlen(pcstrData);
	int iW2MLen1 = MultiByteToWideChar(encoding, 0, pcstrData, len, NULL, 0);
	if (iW2MLen1 == 0) {
		// 失敗
		return -1;
	}
	*ppwsRslt = (LPWSTR)malloc((iW2MLen1 + 1)*2);
	if (*ppwsRslt == NULL) {
		// 失敗
		return -1;
	}
	ZeroMemory(*ppwsRslt, (iW2MLen1 + 1) * 2);
	int iW2MLen2 = MultiByteToWideChar(encoding, 0, pcstrData, len, (LPWSTR)*ppwsRslt, iW2MLen1);
	if (iW2MLen2 == 0) {
		// 失敗
		free(*ppwsRslt);
		return -1;
	}
	return iW2MLen2 * 2;
}

int main()
{
	const wchar_t *pwsData =
		L"kotatuinu!\n"
		L"炬燵犬!\n"
		L"コタツイヌ\n";

	printf("データ:%ls\n", pwsData);

	// wchar_t
	size_t len = wcslen(pwsData);
	dumpBin("wchar_t", (LPCSTR)pwsData, len*2);

	// _bstr_t -> BSTR
	_bstr_t bstrData1(pwsData);
	BSTR bsData1 = bstrData1;
	len = SysStringByteLen(bsData1);
	dumpBin("_bstr_t -> BSTR", (LPCSTR)bsData1, len);

	// _bstr_t.copy(false) -> BSTR
	// _bstr_t.copy(false)は_bstr_tのm_wstrメンバの領域を渡す→解放しちゃダメ
	BSTR bsData3 = bstrData1.copy(false);
	len = SysStringByteLen(bsData3);
	dumpBin("_bstr_t.copy(false) -> BSTR", (LPCSTR)bsData3, len);

	// _bstr_t.copy(true) -> BSTR
	// _bstr_t.copy(true)は新たに領域を取得する→SysFreeString()で解放が必要
	BSTR bsData2 = bstrData1.copy(true);
	len = SysStringByteLen(bsData2);
	dumpBin("_bstr_t.copy(true) -> BSTR", (LPCSTR)bsData2, len);
	::SysFreeString(bsData2);

	// _bstr_t -> const char *
	const char *szData1 = (const char*)bstrData1;
	len = strlen(szData1);
	dumpBin("_bstr_t -> const char *", (LPCSTR)szData1, len);

	// wchar->multibyte(encode UTF8)
	LPSTR pszData4;
	int iRtn = convertWideToMulti(pwsData, CP_UTF8, &pszData4);
	len = strlen(pszData4);
	len = _mbstrlen(pszData4);
	dumpBin("wchar->multibyte(encode UTF8)", (LPCSTR)pszData4, len);
	//free(pszData4);

	// wchar->multibyte(encode SHIFT-JIS)
	LPSTR pszData5;
	iRtn = convertWideToMulti(pwsData, CP_ACP, &pszData5);
	len = strlen(pszData5);
	len = _mbstrlen(pszData5);
	dumpBin("wchar->multibyte(encode SHIFT-JIS)", (LPCSTR)pszData5, len);
	free(pszData5);

	// multibyte(UTF-8)->wchar(UNICODE)->multibyte(SHIFT-JIS)
	LPWSTR pwsData1;
	LPSTR pszData6;
	iRtn = convertMultiToWide((LPCSTR)pszData4, CP_UTF8, &pwsData1);
	iRtn = convertWideToMulti(pwsData1, CP_ACP, &pszData6);
	dumpBin("multibyte(UTF-8)->wchar(UNICODE)->multibyte(SHIFT-JIS)", (LPCSTR)pszData6, iRtn);
	free(pszData4);
	free(pszData6);
	free(pwsData1);

	return 0;
}

出力結果

データ:kotatuinu!
wchar_t (address=0x00f5cc5c):
        6b 00 6f 00 74 00 61 00 74 00 75 00 69 00 6e 00
        00 21 00 0a 00 ac 70 f5 71 ac 72 01 ff 0a 00 7a
        80 ff 82 ff 72 ff 87 ff 0a 00
_bstr_t -> BSTR (address=0x00d96da4):
        6b 00 6f 00 74 00 61 00 74 00 75 00 69 00 6e 00
        00 21 00 0a 00 ac 70 f5 71 ac 72 01 ff 0a 00 7a
        80 ff 82 ff 72 ff 87 ff 0a 00
_bstr_t.copy(false) -> BSTR (address=0x00d96da4):
        6b 00 6f 00 74 00 61 00 74 00 75 00 69 00 6e 00
        00 21 00 0a 00 ac 70 f5 71 ac 72 01 ff 0a 00 7a
        80 ff 82 ff 72 ff 87 ff 0a 00
_bstr_t.copy(true) -> BSTR (address=0x00d96a44):
        6b 00 6f 00 74 00 61 00 74 00 75 00 69 00 6e 00
        00 21 00 0a 00 ac 70 f5 71 ac 72 01 ff 0a 00 7a
        80 ff 82 ff 72 ff 87 ff 0a 00
_bstr_t -> const char * (address=0x00d96ee0):
        6b 6f 74 61 74 75 69 6e 75 21 0a e0 78 e0 9d 8c
        81 49 0a ba c0 c2 b2 c7 0a
wchar->multibyte(encode UTF8) (address=0x00d8b6c0):
        6b 6f 74 61 74 75 69 6e 75 21 0a e7 82 ac e7 87
        e7 8a ac ef bc 81 0a ef bd ba ef be 80 ef be 82
        bd b2 ef be 87 0a
wchar->multibyte(encode SHIFT-JIS) (address=0x00d96d30):
        6b 6f 74 61 74 75 69 6e 75 21 0a e0 78 e0 9d 8c
        81 49 0a ba c0 c2 b2 c7 0a
multibyte(UTF-8)->wchar(UNICODE)->multibyte(SHIFT-JIS) (address=0x00d96fb8):
        6b 6f 74 61 74 75 69 6e 75 21 0a e0 78 e0 9d 8c
        81 49 0a ba c0 c2 b2 c7 0a

« 株:現物売り約定(三菱UFJファイナンシャル・グループ、カプコン) | トップページ | 今週届いた本 »

コメント

コメントを書く

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.f.cocolog-nifty.com/t/trackback/117572/70832495

この記事へのトラックバック一覧です: 文字コードについて(主にWindows):

« 株:現物売り約定(三菱UFJファイナンシャル・グループ、カプコン) | トップページ | 今週届いた本 »