「C 文字列操作」の版間の差分

提供:yonewiki
編集の要約なし
 
2,625行目: 2,625行目:




==='''ICU導入'''===
ICUのWebSite<br />
ICUのWebSite<br />
http://site.icu-project.org/<br />
http://site.icu-project.org/<br />

2023年12月7日 (木) 22:41時点における最新版

C++へ戻る


※このページではC言語にも存在していたという意味で記事タイトルがC 文字列操作になっていますが、
C++でも同様です。C++だけの機能がある場合は明記します。

文字列操作

文字列のコピー、連結、比較、一致、区切り文字分割、探索、文字列長取得といった操作を文字列操作と呼びます。上記のような操作はCRT関数として提供されているため、関数の使い方さえ覚えれば簡単に操作できます。 更に、文字列の型変換、文字コード変換、大文字小文字変換、半角文字、全角文字変換、ファイルパス操作、ファイル名操作、拡張子取得といった文字列操作の応用もプログラミングでは必要になってきますし、文字列の検索と置換、ファイルへの入出力といった操作までを習得すると概ねの操作ができるようになります。ちなみに、XMLとか、HTMLとか特定の形式のテキストを取り扱う技術についてはここでは、取り上げません。記述するとしたら別の項目になるかと思います。


上記のように日本語でまとめて言い換えるのは簡単ですが、一つ一つを実際にやってみるとなると、そう簡単でもありません。メモリに保管される文字列を操作するというのは、手続きが複雑です。コピーひとつにしても、どういった文字列を基にして、何文字目から何文字目までをコピーするのか?それにはどれくらいの大きさの文字列長があるのか?どういった変数に格納するのか?領域はちゃんと確保されているのか?手続きがうまくいかなかった場合はどうするのか?と何気に細かいことを教えてあげないと動かないのがコンピュータです。コンピュータ側からすると、人間のようには解釈は出来ない、そのかわりわからないことがあれば、どんな地道なことをしててでも調べてやるから、コンピュータに指令するのに必要な情報をそろえてほしい。


まとめては動作させれないけど、内部的にはひとつづつ着実にコピーして、最終的には全部をコピーしてくれる。なんだか新入社員のようにいちいち指示しないといけないけど、まぁ的確に指示をすればやる気だけはあって、こなしてくれる。新入社員はほっといても成長しますが、コンピュータはほっとくと延々と成長しません。新入社員さんでも、あまりに放置すると教育不足ということで、指導者の責任問題とか、新人さんの人生を曲げてしまうという強烈な負のスパイラルが発生します。人間にはコンピュータにはまだまだ備えることのできない心って奴があります。大事にしないとね。ともかく、コンピュータが勝手にやってくれるようになるには、勝手にやれるようになるための成長するプログラムが必要になります。偉い人たちは地道にやっているようです。


成長云々の件はここでは関係ありませんので、置いておくとして、文字列の操作をひとつづつやってみましょう。(書きかけの記事です。最終更新131128)


文字列操作に関する関数は以下のアドレスのとおりです。
http://msdn.microsoft.com/ja-jp/library/f0151s4x.aspx
上記があれば、自分が説明する必要はもうないかなと思うわけですが、一応、自分なりに。

ちなみにマルチバイト文字関連の操作は関数が用意されていますが、Windowsストアアプリを作成することはできません。詳細は以下のページの説明のとおりです。変換以外の操作はUnicodeで文字列を扱ってください。つまり、以下で紹介する_mbs***系の操作関数は使えないと考えてください。_wcs***系や_tcs***系でワイド文字を扱う関数にする必要があります。 http://msdn.microsoft.com/library/windows/apps/jj606124.aspx

文字列長取得

文字列の長さを知りたいと思うことは、日常ではほとんどないですが、プログラムでは必要になるパターンが多いです。例えば、どれくらいのメモリサイズを確保すれば、いいのか?とか、文字列から抜き出した文字数が長い場合にはスクロールバーをつけたりとか、文字列の長さによって処理を分岐させたりすることもあります。この他、C言語においては、ほとんどの文字列操作関数で、その操作する文字列変数の文字列の長さを引数として与えなければならなかったりと、文字列操作の中でも基本中の基本となる部分です。知能指数の低い自分は文字数の足し算、引き算で間違えたりすることも多いです。うまく数えないと、枠にぴったりはまらないのがプログラム。


リファレンスは以下のとおりです。
http://msdn.microsoft.com/ja-jp/library/78zh94ax.aspx
http://msdn.microsoft.com/ja-jp/library/z50ty2zh.aspx
_tcslen(TCHA型設定時)/wcslen(Unicode設定)/strlen(マルチバイト設定)マルチバイト設定時の日本語対応関数 _mbslenになります。第一引数がNULLのとき、アクセス違反が発生します。


lenの前にnが付くものは第二引数に最大文字数を設定する必要があり、文字列がそれを超えた場合は最大文字数を返します。
_tcsnlen(TCHA型設定時)/wcsnlen(Unicode設定)/strnlen(マルチバイト設定) サフィックスに_sが付く関数では、第一引数がNULLときは戻り値が0になります。
マルチバイト設定時の日本語対応関数 _mbsnlen

_mbstrlenおよび_mbstrnlen は無効なマルチバイトを含む場合に戻り値-1を返します。_mbstrnlen は最大文字を超えると例外処理が発生し戻り値に-1とするとともにEINVAL に errnoを格納します。
更にサフィックスに_lが付くものは最後の引数として、ロケール設定ができます。ロケール設定の無い関数はsetlocale関数で設定された値に従います。


全部で
strlen、strlen_l、wcslen、wcslen_l、_mbslen、_mbslen_l、_mbstrlen、_mbstrlen_l , strnlen、strnlen_s、strnlen_l、wcsnlen、wcsnlen_s、wcsnlen_l、_mbsnlen、_mbsnlen_l、_mbstrnlen、_mbstrnlen_l
これだけの種類があります。


要するに?
_tcslenを使うのが良いでしょう。Locale設定はあらかじめ実施しておくのが良いと思います。strnlen_sやwcsnlen_sはNULLの文字列の場合に0を返してくれるのはなんか良さげですが、文字列の終端が必ず\0で終わっていさえすれば必要のないことです。それはヌル文字でもいいわけです。アドレスがヌルのヌルポインターのときにプログラムが止まるよって言ってます。なんか、いっぱいありすぎて迷うけど…。


_tcslen!


_lが付く関数は個別にLocale変換が必要な場合にのみ使えば良いでしょう。_tcsnlenで最大文字数を設定するのは、一見安全そうですが、なんか無駄なような気がします。あきらかに最大文字数がわかるような場合には、なんか入れておいて_tcsnlen_sってのはいい選択肢だと思います。


#include <tchar.h>
int main() {
	_tsetlocale(LC_ALL, _T("Japanese"));
	const TCHAR *cStr0[]={_T("表示:よねウィキの機能<yonewiki>"),_T("表示:よねウィキの機能1<yonewiki>"),_T("表示:よねウィキの機能2<yonewiki>")};
	for(int i = 0; i < (sizeof(cStr0)/sizeof(*cStr0)); i++){
		_tprintf(_T("%2d/%2d:cStr0[%2d]=%s\nStrCount=%d\n\n"),i, sizeof(cStr0)/sizeof(*cStr0),i, cStr0[i],_tcslen(cStr0[i]));
	}
}

出力結果

 0/ 3:cStr0[ 0]=表示よねウィキの機能<yonewiki>
StrCount=21

 1/ 3:cStr0[ 1]=表示よねウィキの機能1<yonewiki>
StrCount=22

 2/ 3:cStr0[ 2]=表示よねウィキの機能2<yonewiki>
StrCount=22

_mb系の関数を利用するには、#include <mbstring.h>をインクルードする必要があります。

更に_mbslenは文字列長探索をしたい文字列変数の引数が、unsigned char型ですので、wchar_t型、char型の引数では文字列長を探索することは出来ません。あえて実施するならば、reinterpret_cast<unsigned char*>(※charポインタ型※)と強制的なキャストを実施することで、動作させることができます。_mbstrlenはchar型を引数にしますので、wchar_t型を使わない場合の日本語文字列長検索として使うことが出来ます。


但し、この強制キャストは定数文字列const宣言のある文字列に対しては操作が出来ないため、文字列のコピーを作成する必要があります。文字列コピー操作の詳細は次の項目に記載予定なので、順番に読み進めている人は、コピー操作について理解してから戻ってきた方が良いかもしれません。


#include <tchar.h>
#include <mbstring.h>
int main() {
	_tsetlocale(LC_ALL, _T("Japanese"));
	const char  *cStr1[]={"",       "表示:よねウィキの機能<yonewiki>",    "表示:よねウィキの機能1<yonewiki>",    "表示:よねウィキの機能2<yonewiki>"};
	char  **ppcStr1 = new char* [sizeof(cStr1)/sizeof(*cStr1)];
	_tprintf(_T("const マルチバイト文字→マルチバイト文字コピー→_mbslen関数2バイト文字認識文字列長探索\n"));
	for(int i = 0; i < (sizeof(cStr1)/sizeof(*cStr1)); i++){
		ppcStr1[i] = new char[strlen(cStr1[i]) + 1];//まずは単純に文字列をコピーするための領域を確保。
		
		strcpy_s(ppcStr1[i], strlen(cStr1[i]) + 1,cStr1[i]);//コピー

	}
	for(int i = 0; i < (sizeof(cStr1)/sizeof(*cStr1)); i++){
		//強制キャストでunsigned char*型に変換して文字列長を探索。
		printf("%2d/%2d:cStr1[%2d]=%s\nStrCount=%d\n\n",i, sizeof(cStr1)/sizeof(*cStr1),i, ppcStr1[i],_mbslen(reinterpret_cast<unsigned char*>(ppcStr1[i])));
	}
	for(int i = 0; i < (sizeof(cStr1)/sizeof(*cStr1)); i++){
		delete[] *(ppcStr1 + i);
	}
	delete[] ppcStr1;
}

という具合にして、const char→char→_mabslenで文字列長探索が出来ます。出力結果は以下のとおりです。

const マルチバイト文字マルチバイト文字コピー_mbslen関数2バイト文字認識文字列長探索
 0/ 4:cStr1[ 0]=
StrCount=0

 1/ 4:cStr1[ 1]=表示よねウィキの機能<yonewiki>
StrCount=21

 2/ 4:cStr1[ 2]=表示よねウィキの機能1<yonewiki>
StrCount=22

 3/ 4:cStr1[ 3]=表示よねウィキの機能2<yonewiki>
StrCount=22

_mbstrlen関数を使う場合はコピー操作が必要にならず、const宣言している文字列変数を使っての文字列長探索ができます。

#include <tchar.h>
#include <mbstring.h>
int main() {
	_tsetlocale(LC_ALL, _T("Japanese"));
	const char  *cStr1[]={"",       "表示:よねウィキの機能<yonewiki>",    "表示:よねウィキの機能1<yonewiki>",    "表示:よねウィキの機能2<yonewiki>"};
	_tprintf(_T("const マルチバイト文字→_mbstrlen関数2バイト文字認識文字列長探索\n"));
	for(int i = 0; i < (sizeof(cStr1)/sizeof(*cStr1)); i++){
		printf("%2d/%2d:cStr1[%2d]=%s\nStrCount=%d\n\n",i, sizeof(cStr1)/sizeof(*cStr1),i, cStr1[i],_mbstrlen(cStr1[i]));
	}
}

出力結果は以下のとおりです。

const マルチバイト文字_mbstrlen関数2バイト文字認識文字列長探索
 0/ 4:cStr1[ 0]=
StrCount=0

 1/ 4:cStr1[ 1]=表示よねウィキの機能<yonewiki>
StrCount=21

 2/ 4:cStr1[ 2]=表示よねウィキの機能1<yonewiki>
StrCount=22

 3/ 4:cStr1[ 3]=表示よねウィキの機能2<yonewiki>
StrCount=22

といった具合にいろいろな関数があるので、一つづつ掘り下げて理解しておくのも、必要です。こういう違いがあるということを把握していれば、臨機応変に対応しやすくなるかもです。自分は分かったつもりにならずに、常に謙虚に確かめてみる姿勢ってのは、必要なんじゃないかと思います。そして常に、自分が実施するであろうパターンを網羅して確かめる。だから、あえて文字列の配列の場合にどうやればいいのかを探ります。

文字列コピー

文字列のコピー?なんでコピーするの?ってなりそうですけど、変数の代入みたいなものです。元の文字列はとっておいて、コピーした方を加工するということです。あるいはその逆。紙のコピーだと原版が綺麗だけど、原版もコピーも同じだから…
a=13;
b=a;
b=b*b;
b=a;
b=b*b*b;
ってな感じのことやったりしません?aの値は取っておきたい。そういうときに使ったりするのかなぁと思います。
リファレンスは以下のとおりです。
http://msdn.microsoft.com/ja-jp/library/kk6xf663.aspx
http://msdn.microsoft.com/ja-jp/library/td1esda9.aspx
http://msdn.microsoft.com/ja-jp/library/xdsywd25.aspx
http://msdn.microsoft.com/ja-jp/library/5dae5d43.aspx
また、例によってマルチバイト文字版strcpy、ワイド文字版wcscpy、マルチバイト2バイト文字認識版_mbscpy、セキュリティ強化版の_sサフィックスがあります。_s無し版の引数はコピー先文字列変数先頭char型アドレス、コピー元文字列変数先頭char型アドレス(文字列の終端に\0があること)となっています。_mbscpyの引数は第一引数、第二引数共にunsigned charになっています。この微妙に使いにくい引数の異なり…。しょうがないのかなぁ。


そして、_s版は第2引数にコピー先文字列変数先頭char型アドレスの大きさを必要とします。_sが付かない関数で第二引数だったコピー元は第三引数にずれます。strncpyのようにcpyの前にnが付くパターンでマルチバイト文字版、ワイド文字版、マルチ2バイト文字対応版と更には_lサフィックスでロケール個別設定ができる関数があります。マルチバイト2バイト文字対応版にはバイト長を引数とするcpyの前にbが付いたものもあります。> でも…マルチバイト2バイト文字対応文字ってもともと配列の要素一つに1バイトを格納しているから、バイト長で扱っても、要素数で扱っても変わらないから何が違うのか?よくわからない。_mbs系のcpy関数の利点ってなんだろう。このへんはまた今度、考えてみよう。 全部で、

strcpy、wcscpy、_mbscpy , strcpy_s、wcscpy_s、_mbscpy_s ,
strncpy、_strncpy_l、wcsncpy、_wcsncpy_l、_mbsncpy、_mbsncpy_l , strncpy_s、_strncpy_s_l、wcsncpy_s、_wcsncpy_s_l、_mbsncpy_s、_mbsncpy_s_l
_mbsnbcpy、_mbsnbcpy_l , _mbsnbcpy_s、_mbsnbcpy_s_l
これだけあります。


で、結局は_tcsncpy_sを使いなさいってことになります。Unicode設定ならワイド文字版で、文字列長に厳しい設定が必要なwcsncpy_sだね。第二引数は配列の大きさ+終端\0のための要素1つ分。バイト数ではないです。動的に生成した変数の場合は配列の大きさが取得できないので、wcslen(_tcslen)のような文字列長取得関数を使って、戻ってきた値に\0の要素のために1を加算した値が良いです。

#include <iostream>
//#include<locale.h> tchar.hがインクルードされていれば、いらない。
#include<tchar.h>
int main() {
	_tsetlocale( LC_ALL, _T("Japanese"));
	size_t requiredSize = 0;
	size_t sizecStr0=0;

	TCHAR *cStr0[]={_T("表示:よねウィキの機能<yonewiki>"),_T("表示:よねウィキの機能1<yonewiki>"),_T("表示:よねウィキの機能2<yonewiki>")};
	const char *cStr1[]={" ","表示:よねウィキの機能<yonewiki>","表示:よねウィキの機能1<yonewiki>","表示:よねウィキの機能2<yonewiki>"};
	TCHAR **ppcStr0 = new TCHAR*[sizeof(cStr0)/sizeof(*cStr0)];
	char **ppcStr1 = new char*[sizeof(cStr1)/sizeof(*cStr1)];
	unsigned char **ppucStr2;
	ppucStr2 = new unsigned char*[sizeof(cStr1)/sizeof(*cStr1)];
	
	//_tcscpy_s(wcscpy_s)関数での文字列コピー
	for(int i = 0; i < (sizeof(cStr0)/sizeof(*cStr0)); i++){
		ppcStr0[i] = new TCHAR[_tcslen(cStr0[i]) + 1];
		_tcscpy_s(ppcStr0[i], _tcslen(cStr0[i]) + 1,cStr0[i]);//ココ!
	}

	//strcpy_s関数での文字列コピー
	for(int i = 0; i < (sizeof(cStr1)/sizeof(*cStr1)); i++){
		ppcStr1[i] = new char[strlen(cStr1[i]) + 1];
		strcpy_s(ppcStr1[i], strlen(cStr1[i]) + 1,cStr1[i]);//ココ!
	}

	//_mbscpy_s関数での文字列コピー
	for(int i = 0; i < (sizeof(cStr1)/sizeof(*cStr1)); i++){
		sizecStr0 = strlen(ppcStr1[i]) + 1;
		ppucStr2[i] = new unsigned char[sizecStr0];
		_mbscpy_s(ppucStr2[i], sizecStr0,reinterpret_cast<unsigned char*>(ppcStr1[i]));//ココ!
	}

	//出力部分
	for(int i = 0; i < sizeof(cStr0)/sizeof(*cStr0); i++){
		_tprintf(_T("_tcslen(cStr0[i])=%d\nppcStr0=%s,\n cStr0=%s,\n\n"),_tcslen(cStr0[i]),*(ppcStr0 + i),*(cStr0 + i));
	}
	for(int i = 0; i < sizeof(cStr1)/sizeof(*cStr1); i++){
		printf("strlen(cStr1[i])=%d\nppcStr1=%s,\n cStr1=%s,\n\n",strlen(cStr1[i]),*(ppcStr1 + i),*(cStr1 + i));
	}
	for(int i = 0; i < sizeof(cStr1)/sizeof(*cStr1); i++){
		printf("_mbslen(cStr1[i])=%d\nppucStr2=%s,\n cStr1=%s,\n\n",_mbslen(ppucStr2[i]),ppucStr2[i],*(cStr1 + i));
	}

	//動的に確保したメモリの解放処理
	for(int i = 0; i < sizeof(cStr0)/sizeof(*cStr0); i++){
		delete[] *(ppcStr0 + i);
	}
	for(int i = 0; i < sizeof(cStr1)/sizeof(*cStr1); i++){
		delete[] *(ppcStr1 + i);
	}
	for(int i = 0; i < sizeof(cStr1)/sizeof(*cStr1); i++){
		delete[] *(ppucStr2 + i);
	}
	delete[] ppucStr2;
	delete[] ppcStr0;
	delete[] ppcStr1;
	printf("\n");
	return 0;
}

という感じでしたね。無理やり_mbscpy関数を使ってみるとこんな感じですね。unsigned char型は宣言と同時にnew演算子の定義は出来ないようです。const型のcharからはキャストできないから、結局は一度、strcpy_sを使って、char型の文字列に置き換えないといけないです。マルチバイト型の2バイト文字認識操作関数って結局は、char型と同じなので、文字数を数えるときはstrlenで数えないといけない。char型と_mbsxxx関数を行ったり来たりするだけです。
出力結果は以下のとおり

_tcslen(cStr0[i])=21
ppcStr0=表示よねウィキの機能<yonewiki>,
 cStr0=表示よねウィキの機能<yonewiki>,

_tcslen(cStr0[i])=22
ppcStr0=表示よねウィキの機能1<yonewiki>,
 cStr0=表示よねウィキの機能1<yonewiki>,

_tcslen(cStr0[i])=22
ppcStr0=表示よねウィキの機能2<yonewiki>,
 cStr0=表示よねウィキの機能2<yonewiki>,

strlen(cStr1[i])=1
ppcStr1= ,
 cStr1= ,

strlen(cStr1[i])=32
ppcStr1=表示よねウィキの機能<yonewiki>,
 cStr1=表示よねウィキの機能<yonewiki>,

strlen(cStr1[i])=33
ppcStr1=表示よねウィキの機能1<yonewiki>,
 cStr1=表示よねウィキの機能1<yonewiki>,

strlen(cStr1[i])=33
ppcStr1=表示よねウィキの機能2<yonewiki>,
 cStr1=表示よねウィキの機能2<yonewiki>,

_mbslen(cStr1[i])=1
ppucStr2= ,
 cStr1= ,

_mbslen(cStr1[i])=21
ppucStr2=表示よねウィキの機能<yonewiki>,
 cStr1=表示よねウィキの機能<yonewiki>,

_mbslen(cStr1[i])=22
ppucStr2=表示よねウィキの機能1<yonewiki>,
 cStr1=表示よねウィキの機能1<yonewiki>,

_mbslen(cStr1[i])=22
ppucStr2=表示よねウィキの機能2<yonewiki>,
 cStr1=表示よねウィキの機能2<yonewiki>,

上記に加えて、コピーする文字数を引数とするcpyの前にnが付く関数_tcsncpy_s/wcsncpy_s/strncpy_s/_mbsncpy_sにする場合は



		//_tcscpy_s(ppcStr0[i], _tcslen(cStr0[i]) + 1,cStr0[i]);//ココ!
		_tcsncpy_s(ppcStr0[i], _tcslen(cStr0[i]) + 1,cStr0[i], _tcslen(cStr0[i]) + 1);//ココ!


		//strcpy_s(ppcStr1[i], strlen(cStr1[i]) + 1,cStr1[i]);//ココ!
		strncpy_s(ppcStr1[i], strlen(cStr1[i]) + 1,cStr1[i],strlen(cStr1[i]) + 1);//ココ!


		//_mbscpy_s(ppucStr2[i], sizecStr0,reinterpret_cast<unsigned char*>(ppcStr1[i]));//ココ!
		_mbsncpy_s(ppucStr2[i], sizecStr0,reinterpret_cast<unsigned char*>(ppcStr1[i]), _mbslen(reinterpret_cast<unsigned char*>(ppcStr1[i])));//ココ!



と、4つの引数をとるように記述します。第4引数が出力する最大文字の配列数です。出力する文字数と考えることもできます。マルチバイト文字の場合には出力する文字数を指定できるのは効果的で、先頭から何バイト目で区切れば日本語文字が分断されないかの判断もしてくれながらの出力となります。この出力する文字数をあえてバイト単位で指定する_mbsnbcpy_sもあります。ただし、コピー先の文字列の配列はstrlenのようなバイト数分で準備する必要があることに注意が必要です。出力文字数を指定する場合は文字列全体の長さではなく、指定した文字数で必要な文字列バイト数を算出しておいて、メモリを確保するように処理を記述するのが良いかもしれません。ここでは強制キャストを使いましたが、もともとの文字列がunsigned charとして定義されているものをコピーするときに_mbs系の文字列コピーを利用するというのが自然な使い方になります。

文字列連結

文字列の処理において、文字列を抜き出すことと、連結することはプログラミングにおいて、かなり頻繁に必要となる処理です。設定ファイルのようなものを書き出す場合に、設定1の値をAとユーザが指定した場合、文字列リテラルとして、「<設定1="」と「A」と「">」とを繋ぎ合わせて、記憶しておいて、あとで書き出し処理をしたり、あとで設定ファイルの中身を見る時に設定1の値を抜き出すためにAという値を抜き出す。そういう感じです。上記のようなXML形式の設定方法ならパースという手法によって、既に設定属性値と設定値を書き込んだり、読み込んだりするための関数が提供されていたりしますが、同じようなことを自分でやっていくということはあります。


連結は英語でcatenateと言うため、strcatのようなcatというキーワードが関数に使われます。
リファレンスは以下の通りです。
http://msdn.microsoft.com/ja-jp/library/tbyd7s1y.aspx
http://msdn.microsoft.com/ja-jp/library/w6w3kbaf.aspx
http://msdn.microsoft.com/ja-jp/library/td1esda9.aspx
http://msdn.microsoft.com/ja-jp/library/d45bbxx4.aspx
http://msdn.microsoft.com/ja-jp/library/5c6eeh98.aspx
http://msdn.microsoft.com/ja-jp/library/h1x0y282.aspx
多い…
strncat、_strncat_l、wcsncat、wcsncat_l、_mbsncat、_mbsncat_l , strncat_s、_strncat_s_l、wcsncat_s、_wcsncat_s_l、_mbsncat_s、_mbsncat_s_l
strcat、wcscat、_mbscat , strcat_s、wcscat_s、_mbscat_s
_mbsnbcat、_mbsnbcat_l , _mbsnbcat_s、_mbsnbcat_s_l

例によって_sのついた連結先文字列の配列サイズを明記する関数に_lのついた個別ロケール設定関数。そして連結させたい文字列の文字数を記述するncatに文字数をバイト数で指定するnbcat。あとはワイド文字のwcsで始まる関数。マルチバイトのstrで始まる関数。マルチバイト2バイト文字対応の_mbsで始まる関数。全部で22種類。引数は文字列コピーと同じですが、コピー先の変数が既に文字列が格納されてる\0で終わる文字列になっていて、処理をした結果、第一引数の文字列先頭アドレスの中身が文字列連結した結果になるという点が異なると覚えれば、連結とコピーは似ていると覚えればよいかと思います。先の文字列コピーのプログラムのコピーcpy系関数の後ろに連結cat系関数を追加して、文字列を2回繰り返されるようにしたサンプルです。所謂(いわゆる)、手抜きです。ただし、連結関数では連結後の文字列の配列大きさを要求されるため、あらかじめ動的に確保する領域が2倍になっていることに注意して下さい。

#include <iostream>
//#include<locale.h> tchar.hがインクルードされていればいらない。
#include<tchar.h>
int main() {
	_tsetlocale( LC_ALL, _T("Japanese"));
	size_t requiredSize = 0;
	size_t sizecStr0=0;

	TCHAR *cStr0[]={_T("表示:よねウィキの機能<yonewiki>"),_T("表示:よねウィキの機能1<yonewiki>"),_T("表示:よねウィキの機能2<yonewiki>")};
	const char *cStr1[]={" ","表示:よねウィキの機能<yonewiki>","表示:よねウィキの機能1<yonewiki>","表示:よねウィキの機能2<yonewiki>"};
	TCHAR **ppcStr0 = new TCHAR*[sizeof(cStr0)/sizeof(*cStr0)];
	char **ppcStr1 = new char*[sizeof(cStr1)/sizeof(*cStr1)];
	unsigned char **ppucStr2;
	ppucStr2 = new unsigned char*[sizeof(cStr1)/sizeof(*cStr1)];
	
	//_tcscpy_s(wcscpy_s)関数での文字列コピー
	for(int i = 0; i < (sizeof(cStr0)/sizeof(*cStr0)); i++){
		ppcStr0[i] = new TCHAR[(_tcslen(cStr0[i]) * 2) + 1];
		_tcsncpy_s(ppcStr0[i], _tcslen(cStr0[i])  + 1,cStr0[i], _tcslen(cStr0[i]) + 1);//ココ!
		_tcsncat_s(ppcStr0[i], (_tcslen(cStr0[i]) * 2) + 1,cStr0[i], _tcslen(cStr0[i]) + 1);//ココ!
	}

	//strcpy_s関数での文字列コピー
	for(int i = 0; i < (sizeof(cStr1)/sizeof(*cStr1)); i++){
		ppcStr1[i] = new char[(strlen(cStr1[i]) * 2) + 1];
		strncpy_s(ppcStr1[i], strlen(cStr1[i]) + 1,cStr1[i],strlen(cStr1[i]) + 1);//ココ!
		strncat_s(ppcStr1[i], (strlen(cStr1[i]) * 2) + 1,cStr1[i],strlen(cStr1[i]) + 1);//ココ!
	}

	//_mbscpy_s関数での文字列コピー strcat関数部分ですでに2回繰り返しになった文字をさらに繰り返し連結する処理になってます。4回繰り返し文字列になります。
	for(int i = 0; i < (sizeof(cStr1)/sizeof(*cStr1)); i++){
		sizecStr0 = (strlen(ppcStr1[i]) * 2) + 1;
		ppucStr2[i] = new unsigned char[sizecStr0];
		_mbsncpy_s(ppucStr2[i], sizecStr0,reinterpret_cast<unsigned char*>(ppcStr1[i]), _mbslen(reinterpret_cast<unsigned char*>(ppcStr1[i])));//ココ!
		_mbsncat_s(ppucStr2[i], sizecStr0,reinterpret_cast<unsigned char*>(ppcStr1[i]), _mbslen(reinterpret_cast<unsigned char*>(ppcStr1[i])));//ココ!
	}

	//出力部分
	for(int i = 0; i < sizeof(cStr0)/sizeof(*cStr0); i++){
		_tprintf(_T("_tcslen(cStr0[i])=%d\nppcStr0=%s,\n cStr0=%s,\n\n"),_tcslen(cStr0[i]),*(ppcStr0 + i),*(cStr0 + i));
	}
	for(int i = 0; i < sizeof(cStr1)/sizeof(*cStr1); i++){
		printf("strlen(cStr1[i])=%d\nppcStr1=%s,\n cStr1=%s,\n\n",strlen(cStr1[i]),*(ppcStr1 + i),*(cStr1 + i));
	}
	for(int i = 0; i < sizeof(cStr1)/sizeof(*cStr1); i++){
		printf("_mbslen(cStr1[i])=%d\nppucStr2=%s,\n cStr1=%s,\n\n",_mbslen(ppucStr2[i]),ppucStr2[i],*(cStr1 + i));
	}

	//動的に確保したメモリの解放処理
	for(int i = 0; i < sizeof(cStr0)/sizeof(*cStr0); i++){
		delete[] *(ppcStr0 + i);
	}
	for(int i = 0; i < sizeof(cStr1)/sizeof(*cStr1); i++){
		delete[] *(ppcStr1 + i);
	}
	for(int i = 0; i < sizeof(cStr1)/sizeof(*cStr1); i++){
		delete[] *(ppucStr2 + i);
	}
	delete[] ppucStr2;
	delete[] ppcStr0;
	delete[] ppcStr1;
	printf("\n");
	return 0;
}

出力結果

_tcslen(cStr0[i])=21
ppcStr0=表示よねウィキの機能<yonewiki>表示よねウィキの機能<yonewiki>,
 cStr0=表示よねウィキの機能<yonewiki>,

_tcslen(cStr0[i])=22
ppcStr0=表示よねウィキの機能1<yonewiki>表示よねウィキの機能1<yonewiki>,
 cStr0=表示よねウィキの機能1<yonewiki>,

_tcslen(cStr0[i])=22
ppcStr0=表示よねウィキの機能2<yonewiki>表示よねウィキの機能2<yonewiki>,
 cStr0=表示よねウィキの機能2<yonewiki>,

strlen(cStr1[i])=1
ppcStr1=  ,
 cStr1= ,

strlen(cStr1[i])=32
ppcStr1=表示よねウィキの機能<yonewiki>表示よねウィキの機能<yonewiki>,
 cStr1=表示よねウィキの機能<yonewiki>,

strlen(cStr1[i])=33
ppcStr1=表示よねウィキの機能1<yonewiki>表示よねウィキの機能1<yonewiki>,
 cStr1=表示よねウィキの機能1<yonewiki>,

strlen(cStr1[i])=33
ppcStr1=表示よねウィキの機能2<yonewiki>表示よねウィキの機能2<yonewiki>,
 cStr1=表示よねウィキの機能2<yonewiki>,

_mbslen(cStr1[i])=4
ppucStr2=    ,
 cStr1= ,

_mbslen(cStr1[i])=84
ppucStr2=表示よねウィキの機能<yonewiki>表示よねウィキの機能<yonewiki>表示よねウィキの機能<yonewiki>表示よねウィキの機能<yonewiki>,
 cStr1=表示よねウィキの機能<yonewiki>,

_mbslen(cStr1[i])=88
ppucStr2=表示よねウィキの機能1<yonewiki>表示よねウィキの機能1<yonewiki>表示よねウィキの機能1<yonewiki>表示よねウィキの機能1<yonewiki>,
 cStr1=表示よねウィキの機能1<yonewiki>,

_mbslen(cStr1[i])=88
ppucStr2=表示よねウィキの機能2<yonewiki>表示よねウィキの機能2<yonewiki>表示よねウィキの機能2<yonewiki>表示よねウィキの機能2<yonewiki>,
 cStr1=表示よねウィキの機能2<yonewiki>,

文字列比較

数値の比較は配列でないかぎりは単純な1変数同志の比較ですが、文字列は配列全体が一致しているかを確認することになります。かといって、自分で配列全体を相互に比較するようなプログラムを組むという必要はなく、標準で準備されている関数を使う事で比較できます。VB、PHP、Perlのような関数では関数を使わずに、数値と数値を1変数で比較するかのように比較ができる仕組みになっていますが、C言語では関数を使って比較する必要があります。面倒だ(´Д`;)。


この厳密さも、C言語を難しく感じる一つの要素だと思います。配列をひとつづつ確認する作業なんだという意識づけを忘れないという意味では大事なようにも感じます。だから、ちょっとした変更の伴う比較でも、どうすればよいかを考えることができるのだと思います。VBやPHP、Perlのような便利な比較ばかりをやっているとふと、大文字小文字区別なし変換をしたいとか、半角全角区別なし変換をしたいときに、思考が止まってしまう。 そういうときにはPHPやPerl、VBでも一生懸命調べて、C言語の考え方にたどり着いて、比較することになるんでしょうけど…さいしょから、文字列比較とは、こういうものだと知っておけば、それでいいのですから、ネガティブに考えず、これを覚えれば、潰しが効くとポジティブにとらえてやっていきましょう。比較は英語でcompairと表現するため、関数名にはcmpが使われます。半導体製造工程のcmpとは違います。Chemical Micro Polisherだっけ?違った。Chemical Mechanical Polishingだった。


例によって比較の関数もマルチバイト文字、ワイド文字、マルチバイト2バイト文字対応といろいろな関数があります。
strncmp、wcsncmp、_mbsncmp、_mbsncmp_l
http://msdn.microsoft.com/ja-jp/library/vstudio/eywx8zcx.aspx
_strnicmp、_wcsnicmp、_mbsnicmp、_strnicmp_l、_wcsnicmp_l、_mbsnicmp_l
http://msdn.microsoft.com/ja-jp/library/vstudio/chd90w8e.aspx
_stricmp、_wcsicmp、_mbsicmp、_stricmp_l、_wcsicmp_l、_mbsicmp_l
http://msdn.microsoft.com/ja-jp/library/vstudio/k59z8dwe.aspx
strcmp、wcscmp、_mbscmp
http://msdn.microsoft.com/ja-jp/library/vstudio/e0z9k731.aspx
_mbsnbicmp、_mbsnbicmp_l
http://msdn.microsoft.com/ja-jp/library/vstudio/dyhkb6c5.aspx
_mbsnbcmp、_mbsnbcmp_l
http://msdn.microsoft.com/ja-jp/library/vstudio/hce588f2.aspx
strcoll、wcscoll、_mbscoll、_strcoll_l、_wcscoll_l、_mbscoll_l , _stricoll、_wcsicoll、_mbsicoll、_stricoll_l、_wcsicoll_l、_mbsicoll_l, _strncoll、_wcsncoll、_mbsncoll、_strncoll_l、_wcsncoll_l、_mbsncoll_l, _strnicoll、_wcsnicoll、_mbsnicoll、_strnicoll_l、_wcsnicoll_l、_mbsnicoll_l
http://msdn.microsoft.com/ja-jp/library/vstudio/a7cwbx4t.aspx
http://msdn.microsoft.com/ja-jp/library/vstudio/2w46a1da.aspx
http://msdn.microsoft.com/ja-jp/library/vstudio/72c43s4t.aspx
http://msdn.microsoft.com/ja-jp/library/vstudio/a697c234.aspx

多い!更に多い。
けどicmpってついてるやつは、大文字小文字を区別しないと考えればいいし、cmpで終わらない、collはコードページという特殊な考え方が使われること、ncpmは比較する文字数指定あり、_lはロケール個別指定、と考えればかなりすっきり。collによる比較はあとで考えましょう。


#include <iostream>
//#include<locale.h> wchar.hがインクルードされていれば、いらない。
#include<wchar.h>
int main() {
	_wsetlocale( LC_ALL, L("Japanese"));
	size_t sizecStr0=0;
	long iCmpResult =0;
	TCHAR *cStr0[]={_T("表示:よねウィキの機能0<yonewiki>"),_T("表示Nよねウィキの機能12<yonewiki>"),_T("表示:よねウィキの機能2<yonewiki>")};
	const char *cStr1[]={" ","表示:よねウィキの機能<yonewiki>","表示:よねウィキの機能1<yonewiki>","表示:よねウィキの機能2<yonewiki>"};
	TCHAR **ppcStr0 = new TCHAR*[sizeof(cStr0)/sizeof(*cStr0)];
	char **ppcStr1 = new char*[sizeof(cStr1)/sizeof(*cStr1)];
	unsigned char **ppucStr2;
	ppucStr2 = new unsigned char*[sizeof(cStr1)/sizeof(*cStr1)];

	TCHAR **ppcStr10 = new TCHAR*[sizeof(cStr0)/sizeof(*cStr0)];
	char **ppcStr11 = new char*[sizeof(cStr1)/sizeof(*cStr1)];
	unsigned char **ppucStr20;
	ppucStr20 = new unsigned char*[sizeof(cStr1)/sizeof(*cStr1)];
	
	for(int i = 0; i < (sizeof(cStr0)/sizeof(*cStr0)); i++){
		ppcStr0[i] = new TCHAR[(_tcslen(cStr0[i]) * 2) + 1];
		ppcStr10[i] = new TCHAR[(_tcslen(cStr0[i]) * 2) + 1];
		_tcsncpy_s(ppcStr0[i], _tcslen(cStr0[i])  + 1,cStr0[i], _tcslen(cStr0[i]) + 1);//ココ!
		_tcsncpy_s(ppcStr10[i], _tcslen(cStr0[i])  + 1,cStr0[i], _tcslen(cStr0[i]) + 1);//ココ!
		_tcsncat_s(ppcStr0[i], (_tcslen(cStr0[i]) * 2) + 1,cStr0[i], _tcslen(cStr0[i]) + 1);//ココ!
	}

	//strcpy_s関数での文字列コピー
	for(int i = 0; i < (sizeof(cStr1)/sizeof(*cStr1)); i++){
		ppcStr1[i] = new char[(strlen(cStr1[i]) * 2) + 1];
		ppcStr11[i] = new char[(strlen(cStr1[i]) * 2) + 1];
		strncpy_s(ppcStr1[i], strlen(cStr1[i]) + 1,cStr1[i],strlen(cStr1[i]) + 1);//ココ!
		strncpy_s(ppcStr11[i], strlen(cStr1[i]) + 1,cStr1[i],strlen(cStr1[i]) + 1);//ココ!
	}

	//_mbscpy_s関数での文字列コピー
	for(int i = 0; i < (sizeof(cStr1)/sizeof(*cStr1)); i++){
		sizecStr0 = (strlen(ppcStr1[i]) * 2) + 1;
		ppucStr2[i] = new unsigned char[sizecStr0];
		ppucStr20[i] = new unsigned char[sizecStr0];
		_mbsncpy_s(ppucStr2[i], sizecStr0,reinterpret_cast<unsigned char*>(ppcStr1[i]), _mbslen(reinterpret_cast<unsigned char*>(ppcStr1[i])));//ココ!
		_mbsncpy_s(ppucStr20[i], sizecStr0,reinterpret_cast<unsigned char*>(ppcStr1[i]), _mbslen(reinterpret_cast<unsigned char*>(ppcStr1[i])));//ココ!
	}

	//出力部分
	for(int i = 0; i < sizeof(cStr0)/sizeof(*cStr0); i++){
		_tprintf(_T("_tcslen(cStr0[i])=%d\nppcStr0=%s,\n cStr0=%s,\n"),_tcslen(cStr0[i]),*(ppcStr0 + i),*(cStr0 + i));
		for(int k = 0; k < sizeof(cStr0)/sizeof(*cStr0); k++){
			iCmpResult = _tcsncmp(*(ppcStr0 + i),*(ppcStr10 + k),_tcslen(cStr0[i]));
			_tprintf(_T("i=%d, k=%d, iCmpResult=%ld,\n"),i,k,iCmpResult);
		}
		_tprintf(_T("\n"));
	}
	_tprintf(_T("\n"));
	for(int i = 0; i < (sizeof(cStr1)/sizeof(*cStr1)); i++){
		printf("strlen(cStr1[i])=%d\nppcStr1=%s,\n cStr1=%s,\n\n",strlen(cStr1[i]),*(ppcStr1 + i),*(cStr1 + i));
		for(int k = 0; k < (sizeof(cStr1)/sizeof(*cStr1)); k++){
			iCmpResult = strncmp(*(ppcStr1 + i),*(ppcStr11 + k),strlen(cStr1[i]));
			_tprintf(_T("i=%d, k=%d, iCmpResult=%ld,\n"),i,k,iCmpResult);
		}
		_tprintf(_T("\n"));
	}
	for(int i = 0; i < (sizeof(cStr1)/sizeof(*cStr1)); i++){
		printf("_mbslen(cStr1[i])=%d\nppucStr2=%s,\n cStr1=%s,\n\n",_mbslen(ppucStr2[i]),ppucStr2[i],*(cStr1 + i));
		for(int k = 0; k < (sizeof(cStr1)/sizeof(*cStr1)); k++){
			iCmpResult = _mbsncmp(ppucStr2[i],ppucStr20[k],_mbslen(ppucStr2[i]));
			_tprintf(_T("i=%d, k=%d, iCmpResult=%ld,\n"),i,k,iCmpResult);
		}
		_tprintf(_T("\n"));
	}

	//動的に確保したメモリの解放処理
	for(int i = 0; i < sizeof(cStr0)/sizeof(*cStr0); i++){
		delete[] *(ppcStr0 + i);
	}
	for(int i = 0; i < sizeof(cStr1)/sizeof(*cStr1); i++){
		delete[] *(ppcStr1 + i);
	}
	for(int i = 0; i < sizeof(cStr1)/sizeof(*cStr1); i++){
		delete[] *(ppucStr2 + i);
	}
	delete[] ppucStr2;
	delete[] ppcStr0;
	delete[] ppcStr1;
	printf("\n");
	return 0;
}

出力結果

_tcslen(cStr0[i])=22
ppcStr0=表示よねウィキの機能0<yonewiki>表示よねウィキの機能0<yonewiki>,
 cStr0=表示よねウィキの機能0<yonewiki>,
i=0, k=0, iCmpResult=0,
i=0, k=1, iCmpResult=-20,
i=0, k=2, iCmpResult=-2,

_tcslen(cStr0[i])=23
ppcStr0=表示Nよねウィキの機能12<yonewiki>表示Nよねウィキの機能12<yonewiki>,
 cStr0=表示Nよねウィキの機能12<yonewiki>,
i=1, k=0, iCmpResult=20,
i=1, k=1, iCmpResult=0,
i=1, k=2, iCmpResult=20,

_tcslen(cStr0[i])=22
ppcStr0=表示よねウィキの機能2<yonewiki>表示よねウィキの機能2<yonewiki>,
 cStr0=表示よねウィキの機能2<yonewiki>,
i=2, k=0, iCmpResult=2,
i=2, k=1, iCmpResult=-20,
i=2, k=2, iCmpResult=0,


strlen(cStr1[i])=1
ppcStr1= ,
 cStr1= ,

i=0, k=0, iCmpResult=0,
i=0, k=1, iCmpResult=-1,
i=0, k=2, iCmpResult=-1,
i=0, k=3, iCmpResult=-1,

strlen(cStr1[i])=32
ppcStr1=表示よねウィキの機能<yonewiki>,
 cStr1=表示よねウィキの機能<yonewiki>,

i=1, k=0, iCmpResult=1,
i=1, k=1, iCmpResult=0,
i=1, k=2, iCmpResult=1,
i=1, k=3, iCmpResult=1,

strlen(cStr1[i])=33
ppcStr1=表示よねウィキの機能1<yonewiki>,
 cStr1=表示よねウィキの機能1<yonewiki>,

i=2, k=0, iCmpResult=1,
i=2, k=1, iCmpResult=-1,
i=2, k=2, iCmpResult=0,
i=2, k=3, iCmpResult=-1,

strlen(cStr1[i])=33
ppcStr1=表示よねウィキの機能2<yonewiki>,
 cStr1=表示よねウィキの機能2<yonewiki>,

i=3, k=0, iCmpResult=1,
i=3, k=1, iCmpResult=-1,
i=3, k=2, iCmpResult=1,
i=3, k=3, iCmpResult=0,

_mbslen(cStr1[i])=1
ppucStr2= ,
 cStr1= ,

i=0, k=0, iCmpResult=0,
i=0, k=1, iCmpResult=-1,
i=0, k=2, iCmpResult=-1,
i=0, k=3, iCmpResult=-1,

_mbslen(cStr1[i])=21
ppucStr2=表示よねウィキの機能<yonewiki>,
 cStr1=表示よねウィキの機能<yonewiki>,

i=1, k=0, iCmpResult=1,
i=1, k=1, iCmpResult=0,
i=1, k=2, iCmpResult=1,
i=1, k=3, iCmpResult=1,

_mbslen(cStr1[i])=22
ppucStr2=表示よねウィキの機能1<yonewiki>,
 cStr1=表示よねウィキの機能1<yonewiki>,

i=2, k=0, iCmpResult=1,
i=2, k=1, iCmpResult=-1,
i=2, k=2, iCmpResult=0,
i=2, k=3, iCmpResult=-1,

_mbslen(cStr1[i])=22
ppucStr2=表示よねウィキの機能2<yonewiki>,
 cStr1=表示よねウィキの機能2<yonewiki>,

i=3, k=0, iCmpResult=1,
i=3, k=1, iCmpResult=-1,
i=3, k=2, iCmpResult=1,
i=3, k=3, iCmpResult=0,

と、こんな感じです。結果を見るとわかるのですが、1,-1,0で結果が表現され、先頭から比較して、一番最初に異なる文字同志を比較したときの結果、文字コードの値が大きかったか小さかったかで、1と-1とに結果が分かれます。これを使って昇順に並べたりすることも出来ます。もちろん0が返ってきたときは、完全一致です。等しかったということになります。


少し変わっているのは、関数によっては、文字コードの差分値を返すものもあるという点です。_mbs系では文字コードの差分を返すことは無いようです。cmpの前にiが付く関数は差分を返すようです。ただし_mbsnicmpや_mbsicmpのような関数は差分は返しません。wcs系の関数は差分を返します。このことで差分が数値コードによる部分である場合は、その差は数字の差でもあり、一文字(一桁)の数学的な差を計算するのと同様の処理になったり、アルファベットによる連番と考えた場合でも、その差を計算していることにもなります。2バイト文字では差が大きな数値になりますが、符号付きint型で十分に収まる値です。今回は実験的にlongを使いましたが、無駄足でした。仕様のとおりに動作しますね。


但し、差分の件については、仕様に明記されていませんので、実際に利用する関数での動作を確かめてから使う方が良いと思います。仕様に明記されているのは0より大きい値を返すか小さい値を返すかのどちらかとなっています。正確なVisual C++に限らない言語仕様を検索してみたのですが、わかりませんでした。coll系の比較関数は現在のコードページに従いますので、locale情報を指定しない状態では、各PCのコードページに従います。コードページというのはcp932のような具体的な文字コードのことです。cpの後に続く数字で文字コードセットは分類されています。特段の理由がなければ、使うことは無いでしょう。selocale関数でも引数を指定しなければ各PCのコードページに従いますから、setlocaleで指定できない理由があるという特殊なケースになりそうです。それか、cmpという関数が嫌いでcollが良いと思うかとかですね。ひょっとしたら差分戻り値の件で動作が異なるかもしれません。あとで確認をしておきたいと思います。とてつもなく長いですが、その確認をしてみた結果が以下のものでして、

文字列操作 文字列比較 実行結果1

528行目から534行目のように同じ'Y'0x79大文字と'y'0x89小文字の比較でありながらも、strcollでは1とstrcmpでは-1とでは結果が異なります。asciiコードでは小文字の方が大きな数字の文字コードが割り振られているため、strcmpのように Y と y の差は0x89 - 0x79で負の数値となり -1 となることを期待しますが、collは現在のコードページかつ辞書式順序を使うために小文字よりも大文字が後ということで1になります。辞書式順序って何だ?と思いつつあるのが131202時点の状況でして、さらに調査をすすめたものが

文字列操作 文字列比較 実行結果2

で、

  • 大小のみの判定
wcscmp

wcsicoll/wcscoll/wcsnicoll/_wcsncoll

strcmp/strncmp

strcoll/stricoll/

_mbscmp/_mbsicmp/_mbsncmp/_mbsnicmp/_mbscoll/_mbsicoll/_mbsncoll/_mbsnicoll


但し、半角スペースと-全角の比較が発生すると符号付き4byteの最大値を返す。2147483647=21億4748万4647
_strnicoll/_strncoll


  • 差分を返す
wcsicmp/wcsnicmp/wcsncmp/


stricmp/_strnicmp


という具合の動作であります。collの特徴的なのは辞書順と呼んでいる比較の概念だと思います。ASCIIコードでは大文字と小文字とでは、小文字の方が大きい文字コード番号が付与されていますが、wcs**coll系の比較をする関数では、小文字の方が値が小さいものとして判定してくれます。漢字の範囲になるとロケールで指定したcp932の文字コード順で比較してくれます。阿という文字と哀という文字はUnicode(UTF16)では阿-0x963F 哀-0x54C0 表-0x8868であり、Shift_JISのcp932では阿-0x88A2 哀-0x88A3 表-0x955Cと定義されていますから、阿<哀<表 のように比較をしてくれます。coll系の関数を使わない場合は 哀<表<阿のように比較されます。この阿や哀や表という名前のファイル名のテキストをWindowsのエクスプローラで昇順表示すると、この順番になることも確認できます。Shift_JISコード順でソートされてるんだなぁと確認が出来ると思います。Cp932の半角記号あたりの辞書順ってのは、どうなってるんでしょうね。これもまた今度しらべてみたいと思います。ファイル名に使えない文字あたりはどんな順番なんだろうか?


coll系の比較の中身を見るための関数としては、strxfrm関数がある。これがロケール設定にしたがった照合順序文字列を生成してくれる。これは文字列ではあるが、書き出すことは出来ない照合のための文字コードだと考えればよい。中身をみたければstrxfrmで変換してバイトコードを確認することができるが、ロケール設定によってはなぜこんな変換になるんだ?というような不思議な変換がなされる。とくにen-US.utf8とかになると発音記号のアクセントや、特殊記号を別の辞書順と管理するため、1つの文字列が3つに分割され、1文字でもトークンとなるバイトコードが2つと文字列、アクセント、大文字小文字で3つのバイトコードで5バイトになる。更に特殊記号がひとつでも現れる文字列なら4つに分割され、特殊記号の使われる位置情報まで生成される。このような照合順序規則がロケールごとに存在する。coll系の比較関数はとんでもなく複雑な処理を行う比較関数なのだ。それゆえ通常のcmp系の関数と比べても、処理時間がかかる。localeごとの照合順序について理解しているならば、strxfrmで返還後のコードを使って、strcoll関数のような比較処理を高速化することも可能となる。ロケールによる処理も含めた関数の利用が必要になるケースもあるかもしれないが、ロケールの特徴を理解して、使うのも大事だし、使いどころが肝心となる。

★豆知識
Unicodeの符号化方式UTF-16とは…
日本語の範囲ではUTF16は2バイト文字と考えて良いですが、Unicode全体では0x10FFFFまで利用することになっていますので、0x10000以上の値を持つ文字コードは4バイト文字になります。この0x10000以上の値になる場合は、ビット列で表現すると110110????xxxxxx 110111xxxxxxxxxxのような形式になり????の部分のには上位ビットと下位ビットを16ビットずつに分けた時に0xUUUUDDDDと表記するならば、0xUUUUは0x0001~0x0010の値となり得て、そのUUUU-1の値が????に入ります。xの部分には下位DDDDのビット列そのものが
入ったような値になります。そういう意味ではUnicodeのUTF-16という符号化方式では16bit(2byte)だったり32bit(4byte)のコードになります。※更に異体字セレクタというコードが付与されるケースがあり、付与される場合はUnicode文字番号つづけて、E0100、E0101、E0102…といった値が付与されます。UTF-16の2バイトコードであれば、さらにDB40 DD00という4バイトが付与され一文字が6文字になります。似ている文字をこのセレクタ番号によって切り替えるという仕組みです。したがって2バイト文字は6バイトに、4バイト文字は8バイトになります。テキストエディタおよびフォントも対応していないと使えないような特殊な文字ですが、存在している以上、対応しなければならない可能性はあります。現状はあまり対応されていないようですが、メモ帳でさえも異体字セレクタに対応しているというのが現実です。

Unicodeの符号化方式UTF-8とは…
UTF-8はASCIIコードの7ビット文字は1バイトで表し、それ以降の2バイト文字は3バイトで表すような符号化方式です。この方式でUnicode全体を表現するには最大6byteを使います。Unicodeの上位2Byteが0x1~0x10までの値しか使わないので6バイトで表せます。しかしながら日本語のほとんどは2byte文字で表現できるので、1文字あたり3バイトになるのはUTF16が2倍増しなのに対して、2.5倍増しとなるのがデメリットです。但し、半角英数文字が登場する比率で、そのデメリットは削減できる可能性もあります。日本語を10文字打ったら20byte、7文字打ったら、14byteだから+6文字を半角文字という具合です。7+6だったから、おおよそ半分半分の文字数がいいみたいね。それほど半角英数文字を使うのは技術情報を書く文書くらいだと思いますが…。その人の趣味によって、仕事によって、損得が変わるというものです。UTF-8はかなり広い範囲で使われています。それは英語圏の人が、日本語のような2バイト文字による被害をまったく被らないからだと思います。
ちなみに符号化方式によると2バイト文字はビット列を
x0,x1,x2,x3, x4,x5,x6,x7, x8,x9,xA,xB, xC,xD,xE,xFと表現すると、
(1110),(x0,x1,x2,x3),(10,x4,x5),(x6,x7, x8,x9),(10,xA,xB),(xC,xD,xE,xF)となります。
文字の先頭に3バイトなら3つの1が付き、1バイト目を構成し、 次のバイト以降はバイト先頭に全て10をつける。おかげさまで、1バイト目の下位4bitと、3バイト目の下位4bitの16進数だけはそのままだけど、他のbitは16進表記が、変化します。読み起こすのは大変ですね。文字列の最初を見つけたら、そこから文字列に起こしていけば良いですが、新しい変換表を考えないと直感的にはUnicode文字のコード表には表現できないですね。UTF-8の場合、異体字セレクタはF3 A0 84 80という4バイトが不可されて、3バイト文字は7バイト 4バイト文字は8バイトになります。セレクタがひとつ変化する都度最後の2バイトが80→81→82と変化していきます。

文字列区切り文字分割

文字列の中に区切りがある場合ってのは、例えば、


Sweet Refrain/Perfume,雨のち晴レルヤ/ゆず,東京デスティニー/ポルノグラフィティ,熱愛発覚中/椎名林檎と中田ヤスタカ(CAPSULE),STORY OF MY LIFE/ONE DIRECTION,THE SEVEN SEAS/THE BAWDIES,手紙/ナオト・インティライミ,SLY/RIP SLYME,閃光 feat.10-FEET/東京スカパラダイスオーケストラ,風は西から/奥田民生,DIAMOND SKIN/GLAY,SO RIGHT/三代目 J Soul Brothers from EXILE TRIBE,White Winter Love。/ハジ→,APPLAUSE/LADY GAGA,ピタカゲ from「COUP D'ETAT[+ONE OF A KIND&HEARTBREAKER]」/G-DRAGON (from BIGBANG),LEMON/The Birthday,もったいないとらんど/きゃりーぱみゅぱみゅ,僕らの物語/GReeeeN,Lily/Dragon Ash,START IT AGAIN/AK-69,Very Merry Xmas/東方神起,SURVIVAL/EMINEM,ウォーリーヒーロー/KANA-BOON,あなたへ/エレファントカシマシ,キラーボール/ゲスの極み乙女。,Babies are popstars/松任谷由実,ROCK N ROLL(日本語字幕入り)/AVRIL LAVIGNE,HOT SHOT/GENERATIONS from EXILE TRIBE,守ってあげたい/JUJU,3 2 1/SHINee,Always/斉藤和義,ファンファーレがきこえる/Base Ball Bear,X'masラブストーリー。/ソナーポケット,Missing/androp,ラストバージン/RADWIMPS,エデン(lyric ver)/Aqua Timez,クルクル/e-girls,WHO'S NEXT/SiM,Time goes by/URATA NAOYA,Bi-Li-Li Emotion/Superfly,One day/TOKYO No.1 SOUL SET,粉雪/BENI,FEEVEER/MO'SOME TONEBENDER,No.525300887039/supercell,Hello\,999/N'夙川BOYS,Every Hero/kaho,黒猫?Adult Black Cat?/Acid Black Cherry,指でキスしよう/東京カランコロン,m@u/後藤まりこ,海と花束/きのこ帝国


のようなヒットチャートベスト50があったとしたら , カンマでチャートが区切られていて、 / スラッシュで曲名とアーチスト名が区切られていることになります。


こういった区切り文字毎に見ると意味が発生する最小の単位をトークンと言います。区切る文字によっては更に小さな意味を持つこともありますが、区切り文字毎に文字を抜き出そうとする文字分割処理そのものをトークンと呼んでいます。従って、関数名にはtokenのtokをとったような関数名が使われます。これまでの関数もそうでしたが、やっぱりいろんな種類のstrtok関数が準備されていまして…


strtok、_strtok_l、wcstok、_wcstok_l、_mbstok、_mbstok_l ,
http://msdn.microsoft.com/ja-jp/library/vstudio/2c8d19sb.aspx
strtok_s、_strtok_s_l、wcstok_s、_wcstok_s_l、_mbstok_s、_mbstok_s_l
http://msdn.microsoft.com/ja-jp/library/vstudio/ftsafwz3.aspx


比較関数よりは少ないけど、やっぱりマルチバイト版、マルチバイト2バイト文字対応版、ワイド文字版、それに_sをつけた文字列長を指定するセキュア版と個別ロケール指定をする_lのロケール版があります。_l版についてはココまで使い方を触れてこなかったのですが、

_locale_t locale;
locale = _tsetlocale(LC_ALL, _T("Japanese"));

として、localeという変数に_locale_t型の値を_tsetlocaleの返り値として格納しておいたものを関数の最後の引数にします。
コマンドプロンプトで動作確認してるだけの段階でlocale変数を利用しても、文字化けしか起こせないと思うので、テキスト出力とかを覚えてから試してみるといいかもしれません。


strtok系の関数では主要な引数が2つで、第1引数には区切り文字を含んだ大元の文字列変数の先頭アドレスの値。第2引数には区切り文字の先頭アドレスの値。","のように文字列リテラルを直接指定することもできますし、複数の区切り文字を含めて",/"とすることもできます。このような文字列が格納されてる変数の先頭アドレスを含んだポインタ変数あるいは、アドレスを返す配列名を指定すること(結局ポインタ変数みたいなものですが…)ができます。第1引数の文字列は書き換え可能な変数でないと駄目です。const char[]="…"のように定義している書き換えできない変数を引数にするとエラーになります。なぜならば、この関数は第2引数で指定している区切り文字を発見したら、その文字が格納されているアドレス部分、配列要素部分の中身を文字列終端を意味する\0に書き換えるからです。つまり、第1引数は、元の状態ではなくなるので、あらかじめバックアップが必要ならコピーをして、保管しておかなければなりません。そして、区切り文字が見つかると先頭アドレスから\0に置き換えた部分までの文字列を返り値として処理を終了します。


え?1つ目以降の区切り文字の結果は?


そうですよね…。2回目の区切り文字探索以降について、この関数の使い方は特殊でして、最初の1回目の区切り文字探索時は上記に記述したような引数で良いのですが、2回目以降の区切り文字探索では第1引数にNULLを設定し、最初に探索した際に\0置き換えた区切り文字の次のアドレスから、第2引数で指定した区切り文字を探索します。それで見つかった区切り文字をまた\0に置き換えて、探索開始アドレスから、\0までの文字列を返り値として処理が終わります。


え?それ以降は?


そうですよね…。探索の結果、区切り文字がみつからなくなるまで、繰り返すようなプログラムにして、ひとつづつ掘り起こすという地味な処理になります。 結果を文字列配列に格納したい場合は、自分で文字列配列を準備して、順番に格納しなければならないのです。区切り文字が何回現れて、いくつの文字が区切られた文字列が返ってきたのかも知りたい場合は、自分で回数を記録する処理が必要になります。


Perlなら見つかった分だけ勝手に配列を作ってくれるような関数だったぞ!って思っている人もおられるかもしれませんが、C++ではそこまで便利な標準関数はないです。ご自分でどうぞお好きなように使って下さい。みたいな感じ?そういうことです。2回目以降で元の文字列を指定せず、NULLを指定することからも、察知できることですが、二つ以上の区切り文字を含めた文字列を交互に、作業することもできませんのであしからず。一つ目が終わってから次の文字列って感じです。不思議な仕組みですよね。想像するに、文字列の後ろからなんらかの文字が現れて次に現れる\0がトークン検索開始位置の文字列になってるのかもしれません。このあたりは実験してみることによってあきらかになるかと思います。


区切り文字として複数の文字を指定できますが、それぞれ1文字が独立した区切り文字ですので、// のような2文字以上で構成される区切り文字はこの関数だけでは対応できません。新しい手法が必要になります。

#include <iostream>
#include <tchar.h>
#include <mbstring.h>
//#include <locale.h>
int main() {
  _tsetlocale(LC_ALL, _T("Japanese"));
    _locale_t locale;
  locale = _create_locale(LC_ALL, "Japanese");
 
  int iCmpComma=0;
  int iCmpYen=0;
  int iCmpSlash=0;
  int iChartCommaCnt=0;
 
  TCHAR *cStr0 = _T("Sweet Refrain/Perfume,雨のち晴レルヤ/ゆず,東京デスティニー/ポルノグラフィティ,\\熱愛発覚中/椎名林檎と中田ヤスタカ(CAPSULE),STORY OF MY LIFE/ONE DIRECTION,THE SEVEN SEAS/THE BAWDIES,手紙/ナオト・インティライミ,SLY/RIP SLYME,閃光 feat10-FEET/東京スカパラダイスオーケストラ,風は西から/奥田民生,DIAMOND SKIN/GLAY,SO RIGHT/三代目 J Soul Brothers from EXILE TRIBE,White Winter Love。/ハジ→,APPLAUSE/LADY GAGA,ピタカゲ from「COUP D'ETAT[+ONE OF A KIND&HEARTBREAKER]」/G-DRAGON (from BIGBANG),LEMON/The Birthday,もったいないとらんど/きゃりーぱみゅぱみゅ,僕らの物語/GReeeeN,Lily/Dragon Ash,START IT AGAIN/AK-69,Very Merry Xmas/東方神起,SURVIVAL/EMINEM,ウォーリーヒーロー/KANA-BOON,あなたへ/エレファントカシマシ,キラーボール/ゲスの極み乙女。,Babies are popstars/松任谷由実,ROCK N ROLL(日本語字幕入り)/AVRIL LAVIGNE,HOT SHOT/GENERATIONS from EXILE TRIBE,守ってあげたい/JUJU,3 2 1/SHINee,Always/斉藤和義,ファンファーレがきこえる/Base Ball Bear,X'masラブストーリー。/ソナーポケット,Missing/androp,ラストバージン/RADWIMPS,エデン(lyric ver)/Aqua Timez,クルクル/e-girls,WHO'S NEXT/SiM,Time goes by/URATA NAOYA,Bi-Li-Li Emotion/Superfly,One day/TOKYO No1 SOUL SET,粉雪/BENI,FEEVEER/MO'SOME TONEBENDER,No525300887039/supercell,Hello\\,999/N'夙川BOYS,Every Hero/kaho,黒猫?Adult Black Cat?/Acid Black Cherry,指でキスしよう/東京カランコロン,m@u/後藤まりこ,海と花束/きのこ帝国");
  TCHAR *pcStr0 = new TCHAR[(_tcslen(cStr0)) + 1];
  TCHAR *pcStr1 = new TCHAR[(_tcslen(cStr0)) + 1];
  TCHAR *pcStr2 = new TCHAR[(_tcslen(cStr0)) + 1];
  TCHAR *pcStrTmp;
  TCHAR *pcStrTmp2;
  TCHAR *tcharComma = _T(",");
  TCHAR *tcharYen = _T("\\");
  TCHAR *tcharSlash = _T("/");
  for(unsigned int i = 0; i < _tcslen(cStr0); i++){
    iCmpComma = _tcsncmp(&cStr0[i], tcharComma, 1);
    if(i > 0){
       iCmpYen = _tcsncmp(&cStr0[i - 1] , tcharYen, 1);//コンマ区切りだけど曲名やアーチスト名そのものに,が使われている場合は
                                                     //予めデータを\,のようにエスケープするというデータ規則が必要。,の前に\が無いか確認して配列サイズを確保。
    }
    if( iCmpComma == 0 && iCmpYen != 0){
      iChartCommaCnt++;
    }
  }
  TCHAR ***ppcStrChart = new TCHAR**[iChartCommaCnt + 1];
  TCHAR **ppcStrChartTmp = new TCHAR*[iChartCommaCnt + 1];
  for(int i = 0; i < iChartCommaCnt + 1;i++){
    *(ppcStrChart + i) = new TCHAR*[2];//曲名とアーチスト名の2要素固定
  }
 
  TCHAR *next_token1 = NULL;
  TCHAR *next_token2 = NULL;
 
  _tcsncpy_s(pcStr0, _tcslen(cStr0)  + 1,cStr0, _tcslen(cStr0) + 1);//ココ!
  pcStr1 = _tcstok_s(pcStr0,_T(","), &next_token1);//establish 1回目の呼び出し 
  {
 
  int i = 0;
  bool bStopIncriment = false;
  bool bStopIncriment2 = false;
    while(pcStr1 != NULL && i < (iChartCommaCnt + 1)){
 
      if (bStopIncriment)//区切り文字の前に\が見つかった場合はiを加算しない。
      {
        pcStrTmp = new TCHAR[_tcslen(ppcStrChartTmp[i]) + 1];//現在の文字列最後に区切り文字ではない,があるtmp文字列値バックアップをとるための容量を確保。
        _tcsncpy_s(pcStrTmp, _tcslen(ppcStrChartTmp[i]) + 1, ppcStrChartTmp[i], _tcslen(ppcStrChartTmp[i]) + 1 );//そしてバックアップ変数へコピー
        delete[] ppcStrChartTmp[i];//tmp値を一旦クリア。
        ppcStrChartTmp[i] = new TCHAR[_tcslen(pcStrTmp) + _tcslen(pcStr1) + 1];//もう一度容量を確保しなおす。バクアップ変数の長さ+次の区切り文字までの長さ
        _tcsncpy_s(ppcStrChartTmp[i], _tcslen(pcStrTmp) + _tcslen(pcStr1) + 1 ,pcStrTmp, _tcslen(pcStrTmp));//バックアップ変数と今回の区切り文字を繋ぐ。
        _tcsncat_s(ppcStrChartTmp[i], _tcslen(pcStrTmp) + _tcslen(pcStr1) + 1 ,pcStr1, _tcslen(pcStr1));//バックアップ変数と今回の区切り文字を繋ぐ。
        _tprintf(_T("i=%2d,ChartTmp1=%s; StrTemp=%s;\n"),i,ppcStrChartTmp[i],pcStrTmp);
        delete[] pcStrTmp;//バックアップ変数は利用終了。解放。
      }
      iCmpYen = _tcsncmp(&pcStr1[_tcslen(pcStr1)-1],tcharYen,1);//お次の区切り文字は,区切りにしたpcStr1の最後の文字は\ではなかったか確認?
      if(iCmpYen != 0){
 
        //\以外の通常処理
        if(!bStopIncriment){
          ppcStrChartTmp[i] = new TCHAR[_tcslen(pcStr1) + 1];//区切り文字の長さで文字配列配列i番目を確保
          _tcsncpy_s(ppcStrChartTmp[i],_tcslen(pcStr1) + 1,pcStr1,_tcslen(pcStr1) + 1);//正式な区切り文字なので、区切りまでをコピーして格納。i番目の処理は終了。        
        }
        pcStrTmp2 = new TCHAR[_tcslen(ppcStrChartTmp[i]) + 1];
        _tcsncpy_s(pcStrTmp2,_tcslen(ppcStrChartTmp[i]) + 1,ppcStrChartTmp[i],_tcslen(ppcStrChartTmp[i]) + 1);//正式な区切り文字なので、区切りまでをコピーして格納。i番目の処理は終了。        
        ppcStrChart[i][0] = new TCHAR[_tcslen(pcStrTmp2) + 1];
 
        //完全な入れ子状態。, 区切り処理を / 区切り処理に変えただけ。関数化すればスッキリする。けどそんな関数を作るのが目的ではないので、敢えてコレ。*****************
 
 
        pcStr2 = _tcstok_s(pcStrTmp2, _T("/"), &next_token2);//establish 1回目の呼び出し 
 
 
        while(pcStr2 != NULL){
          if(bStopIncriment2){
            pcStrTmp = new TCHAR[_tcslen(ppcStrChart[i][0]) + 1];//現在の文字列最後に区切り文字ではない,があるtmp文字列値バックアップをとるための容量を確保。
            _tcsncpy_s(pcStrTmp, _tcslen(ppcStrChart[i][0]) + 1, ppcStrChart[i][0], _tcslen(ppcStrChart[i][0]) + 1 );//そしてバックアップ変数へコピー
            delete[] ppcStrChart[i][0];//tmp値を一旦クリア。
            ppcStrChart[i][0] = new TCHAR[_tcslen(pcStrTmp) + _tcslen(pcStr2) + 1];//もう一度容量を確保しなおす。バクアップ変数の長さ+次の区切り文字までの長さ
            _tcsncpy_s(ppcStrChart[i][0], _tcslen(pcStrTmp) + _tcslen(pcStr2) + 1 ,pcStrTmp, _tcslen(pcStrTmp));//バックアップ変数と今回の区切り文字を繋ぐ。
            _tcsncat_s(ppcStrChart[i][0], _tcslen(pcStrTmp) + _tcslen(pcStr2) + 1 ,pcStr2, _tcslen(pcStr2));//バックアップ変数と今回の区切り文字を繋ぐ。
 
            delete[] pcStrTmp;//バックアップ変数は利用終了。解放。
          }
          iCmpSlash = _tcsncmp(&pcStr2[_tcslen(pcStr2) - 1],tcharSlash,1);
 
          if(iCmpSlash != 0){
            if(!bStopIncriment2){
 
              ppcStrChart[i][0] = new TCHAR[_tcslen(pcStr2) + 1];
              _tcsncpy_s(ppcStrChart[i][0],_tcslen(pcStr2) + 1,pcStr2,_tcslen(pcStr2) + 1);//正式な区切り文字なので、区切りまでをコピーして格納。i 0番目の処理は終了。        
 
            }
            bStopIncriment2 = false;
          }
          else{
            ppcStrChart[i][0] = new TCHAR[_tcslen(pcStr2) + 1];//まずは区切り文字までの長さより1文字、短い領域を確保。
            _tcsncpy_s(ppcStrChart[i][0],_tcslen(pcStr2) + 1 ,pcStr1,_tcslen(pcStr2) - 1);//[*]~[*] [\][/][\0]の\以降を削ってコピー。[*]~[*][\0]になる。
            _tcsncat_s(ppcStrChart[i][0],_tcslen(pcStr2) + 1 ,tcharSlash,_tcslen(tcharSlash) + 1);//カンマを付け足す。[*]~[*][/][\0]になる。
            bStopIncriment2 = true;           
          }
 
 
          pcStr2 = _tcstok_s(NULL,_T("/"), &next_token2);//2回目以降の呼び出し 
          ppcStrChart[i][1] = new TCHAR[_tcslen(pcStr2) + 1];
          _tcsncpy_s(ppcStrChart[i][1], _tcslen(pcStr2) + 1, pcStr2, _tcslen(pcStr2) + 1);
          pcStr2 = _tcstok_s(NULL,_T("/"), &next_token2);//2回目以降の呼び出し
        }
 
        //完全な入れ子状態ここまで**************************************************************************************************************************************
        i++;
        bStopIncriment = false;          
        delete pcStrTmp2;
      }
      else{
 
        //\なら,は区切りではないので、ppcStrChartTmp[i]を再作成して、1文字分短くして、\,→,と扱いtmpへ格納。        
        ppcStrChartTmp[i] = new TCHAR[_tcslen(pcStr1) + 1];//まずは区切り文字までの長さより1文字、短い領域を確保。
        _tcsncpy_s(ppcStrChartTmp[i],_tcslen(pcStr1) + 1 ,pcStr1,_tcslen(pcStr1) - 1);//[*]~[*] [\][,][\0]の\以降を削ってコピー。[*]~[*][\0]になる。
        _tcsncat_s(ppcStrChartTmp[i],_tcslen(pcStr1) + 1 ,tcharComma,_tcslen(tcharComma) + 1);//カンマを付け足す。[*]~[*][,][\0]になる。
        bStopIncriment = true;           
      }
 
      pcStr1 = _tcstok_s(NULL,_T(","),&next_token1);//2回目以降の呼び出し
    }
  }
  _tprintf(_T("{| style=\"color:black; background-color:#ffffff;\" cellpadding=\"3\" cellspacing=\"0\" border=\"1\"\n"));
  for(int i = 0; i < iChartCommaCnt;i++){
    _tprintf(_T("|%s\n|%s\n|-\n"),ppcStrChart[i][0],ppcStrChart[i][1]);
  }
  _tprintf(_T("|%s\n|%s\n|}\n"),ppcStrChart[iChartCommaCnt][0],ppcStrChart[iChartCommaCnt][1]);
 
    printf("%d\n",iChartCommaCnt);
  for(int i = 0;i < iChartCommaCnt + 1;i++){
    delete[] *(ppcStrChartTmp + i);
  }
  delete[] ppcStrChartTmp;

  for(int i = 0;i < iChartCommaCnt + 1 ;i++){
    for(int j = 0;j < 2 ;j++){
      delete[] *(*(ppcStrChart + i) + j);
    }
  }
  for(int i = 0;i < iChartCommaCnt + 1 ;i++){
    delete[] *(ppcStrChart + i);
  }
  delete[] ppcStrChart;
  delete[] pcStr0;
  delete[] pcStr1;
  delete[] pcStr2;
  return 0;
}

という具合にtcstok_s関数を使えば、並列してトークン処理が進められるようです。第3引数の参照というC++独自の型を使って、44行目 78行目での最初の呼び出し(エスタブリッシュとも表現します。エスタブリッシュショットというと撮影なんかでワンシーンの手前に一枚絵の風景絵を置くことで、時間帯を表現したりする手法を指します。夜景がシーンの前に入れば室内に映像が切り替わっても夜の出来事であるように示唆するものです。)をして、111、114、131行目のように連続して現れるであろう区切り文字を検索します。111,114行目では、もう区切り文字が現れることがないことがわかっているのですが、2個目のトークンを取得するために実行したり、ループ処理を終わらせるために再度、実行したりという手法で使っています。アイデア次第でなんでもありです。最初にカンマ区切りでトークンを取得するのですが、その間で、さらにカンマ区切りトークンの中に必ず一度現れる、スラッシュによるトークン処理を入れこんでいます。区切り文字が違うだけで全く同じ処理です。こういうプログラム記法は普通はしません。通常は、関数のようなサブプログラムにして、関数を呼び出すことで同じ処理になるように記述します。C++の場合は関数でなくても、クラスのメンバ関数にしても良い訳です。結局同じことですが…。


このような下手くそなプログラムを動かした結果が以下の通りになります。

Sweet Refrain Perfume
雨のち晴レルヤ ゆず
東京デスティニー ポルノグラフィティ
\熱愛発覚中 椎名林檎と中田ヤスタカ(CAPSULE)
STORY OF MY LIFE ONE DIRECTION
THE SEVEN SEAS THE BAWDIES
手紙 ナオト・インティライミ
SLY RIP SLYME
閃光 feat.10-FEET 東京スカパラダイスオーケストラ
風は西から 奥田民生
DIAMOND SKIN GLAY
SO RIGHT 三代目 J Soul Brothers from EXILE TRIBE
White Winter Love。 ハジ→
APPLAUSE LADY GAGA
ピタカゲ from「COUP D'ETAT[+ONE OF A KIND&HEARTBREAKER]」 G-DRAGON (from BIGBANG)
LEMON The Birthday
もったいないとらんど きゃりーぱみゅぱみゅ
僕らの物語 GReeeeN
Lily Dragon Ash
START IT AGAIN AK-69
Very Merry Xmas 東方神起
SURVIVAL EMINEM
ウォーリーヒーロー KANA-BOON
あなたへ エレファントカシマシ
キラーボール ゲスの極み乙女。
Babies are popstars 松任谷由実
ROCK N ROLL(日本語字幕入り) AVRIL LAVIGNE
HOT SHOT GENERATIONS from EXILE TRIBE
守ってあげたい JUJU
3 2 1 SHINee
Always 斉藤和義
ファンファーレがきこえる Base Ball Bear
X'masラブストーリー。 ソナーポケット
Missing androp
ラストバージン RADWIMPS
エデン(lyric ver) Aqua Timez
クルクル e-girls
WHO'S NEXT SiM
Time goes by URATA NAOYA
Bi-Li-Li Emotion Superfly
One day TOKYO No.1 SOUL SET
粉雪 BENI
FEEVEER MO'SOME TONEBENDER
No.525300887039 supercell
Hello,999 N'夙川BOYS
Every Hero kaho
黒猫?Adult Black Cat? Acid Black Cherry
指でキスしよう 東京カランコロン
m@u 後藤まりこ
海と花束 きのこ帝国

何コレ?って思いました?実はMediaWikiの表形式に変換するプログラムだったりして、出力結果をコピペすると、こういう表になるということです。これは某番組の先週のヒットチャートTOP50ですね。SuperCellのNo.って曲はカッコいいね。曲名の数字は読まなくていいらしい。椎名林檎にきゃりーぱみゅぱみゅ、Greeeen、ゆず、Perfume、東京スカパラダイスオーケストラ。最高っす( ・∀・)b意外と10位以下のあまり、もてはやされない曲でいいのあったりする。JPOPはある程度のカタチがあるよね。そのカタチにはまると、なんか好きな感じになる。歌声だったり、曲の構成だったり、意外性も案外と新しいファクターだよ。この意外性をもとめてるのが若い世代なのかもしれないね。でも、無理しないで王道ってのもいいと思うよ。みんなが好きそうな曲作るってのは、それはそれで凄い事。一部の人間の心を捉える曲もあっていいけど、やっぱ前にでれないのが現実。その間をとってる人もいれば、かたくなに自分のスタイルを貫く人もいる。お金儲け主義の曲って言われてもいいじゃん。みんなが好きな曲ってことだからね。みんなの心を捉えないとお金にならないのが音楽業界。音楽を作りもしないレーベル側に搾取される部分ってのは、なんだかもどかしいよね。でも、育ててもらった恩とか、準備してもらった機材、走り回ってくれるスタッフ、その手配をやってるのはレーベルの努力だからね。会社でいうところの特許は会社員が発明して、お金は8割くらい会社が持っていくっていうそんな感じ。研究させてもらえる環境を提供してるだけでもありがたいと思いなさいということです。それが嫌なら自分で会社たててやってみ?ろくなことなんないぜ?ってそんな感じ。自分で音楽事務所たててうまくいく人なんてそうそういないんだよ。オレは、なんかもうそういうのどうでもよくなってきて、惰性で人生を回転させてるだけ。うっかり幸せな気分でも向上するような出来事にたどり着けたら、人生っては、それなり頑張れば、それなりになるってことを確信して死ねるんだろうし、そうでないとしたら、まぁやっぱこんなもんだよな人生ってのはって心の隅で感じて、半笑いくらいで死ねるような気がする。この先も、どうでもいい。


別にプログラムつくりたいわけでもなし、こんな文書かいてんだから、まぁ分かる人には分かるでしょ。それでいい。マルチバイト版とマルチバイト2バイト対応版の動作も確認したいよね。まぁ時間があったらですね。それと表の先頭に - があったら、空白を付け足す処理と 文字列の中に| があったら、&#x7c;に置き換える処理を入れないと、 mediawikiのXHTML表記の表にならないので、もう少し改良が必要です。今回はたまたま、N'夙川BOYS(ん しゅくがわ ボーイズ)の曲名に , が使われていたので、流石にカンマ区切りのトークン処理を不完全にさせておくのはまずいと思い、ここまでで記述してきた文字列操作だけを使って、対応版のサンプルプログラムを作っておきました。N'夙川BOYS恐るべし!Hello,999か…。曲名って何でもありの自由空間だから規制がない。Unicodeで全部表せたらいいほう。なんかよくわからない記号とか使うこともあるし。なんらかの規制がないとデータベース化する人は大変だろうなぁ。

※文字列配列を2次元配列に、つまり3次元配列にしたあたりから、動的なメモリの解放処理ができなくなった(;´・д・)ナンデ。スキル不足でした。役に立ちそうもないし、挫折かな。変に動的なメモリ確保に拘ったのはコレが初めてだから、ショウガナイか。できるとおもってたけど出来ない。そういうことなのかもしれない。一番したの層の文字列事態の解放はできるんですけど、アドレスが格納された部分の解放を記述するとコンパイルエラーにはならないけど、実行時にプログラムが止まる。

ほんとは

delete[] *(ppcStrChart + i);

ってやって50個の配列に要素[0]と[1]っていうのがぶら下がっているのを解放して、最後に

delete[] ppcStrChart

ってな具合にしなきゃいけいないはずなんですけど…strtokを使うとゴミが残りやすいのかな?解放できなくなる。生成した直後なら消せるから、やっぱなんかあるなコレ。リファレンス書くつもりがどんどん疑問が増えて課題が増えてるという…無力だな。また時間があるときにでも、原因を調べてみよう。恐るべしstrtok…。しばらくは、OS側にあとかたづけは任せてみる。ごめんなさい。ときどき再起動しとけばいいのかな…(´Д`;) おそらくは、一度生成したものを再作成するかもしれないという作り方に問題があるのかもしれない。文字列の長さが確定してから容量を確保するようなプログラムにするべきなんだろうね。次から気を付ける。って今回のサンプルはなおさないらしい。コイツ。

と悩んでおりましたが、解決しました。サンプルプログラムも更新済みなので、なんのこっちゃわからないと思いますが、配列は使用する領域よりひとつ多くとっておかないとdeleteできなくなるみたいです。文字列の終端にあるであろう\0と同じですね。ヌルポインターになるまで消去するということかもしれません。TOP50のランキングなのでTCHAR ***ppcStrChart = new TCHAR**[iChartCommaCnt];としてiChartCommaCnt=49の大きさ50の配列にしていましたが、TCHAR ***ppcStrChart = new TCHAR**[iChartCommaCnt + 1];としたところ、プログラムの最後に記述したdelete[] 関数が動作するようになりました。ふぅ。同じように曲名とアーチスト名ということで*(ppcStrChart + i) = new TCHAR*[1];としていましたが、ここも*(ppcStrChart + i) = new TCHAR*[2];ですね。消すときは、ひとつ少ない値のループ回数になっています。for(int j = 0;j < 2 ;j++)のとおりです。少しすっきりしたけど、なんつうか基本中の基本も知らなかった。いかに動的なメモリの確保をやってこなかったかってことか考えさせられます。プログラムの中でガッツリ確保してました。面倒だもん。でも、ちょっと不思議。それこそなんかゴミが残りそうだけどねぇ。結局また調べなきゃいけないのか(;´・д・)。でも、まぁ上のレベルの配列を消したときに、ぶら下がってる配列が最後まで消えるんだから、普通に考えても消えるわな。本当にそうなのかという、確認だけか。大変ではないね。次の事に進もう。日々、勉強だねぇ。つうか、手遅れ的な感じが否めない。最近、何を学んでも手遅れだと感じてしまう。いやいや、知らないままよりはいいか。ポジティブ。ポジティブ。ん~でも。なんつうか60過ぎてから、プロサッカー選手を目指すみたいな要素も含まれてるような気もする。無理だろみたいなね。プログラムは、まぁ歳食ってからでもやってていいから、気付くのが遅くてもいいことにしとこかな。

文字列の型変換

文字列の型にはchar/*char(LPSTR)、WCHAR/*WCHAR(LPWSTR)、TCHAR/*TCHAR(LPTSTR)がありました。これに変更できない文字列としてconst付きで定義された文字列がありました。これらの型に収められた文字列を相互にやり取りする手法について考えようと思います。文字列操作自体はどれか一つの型を採用するのですが、誰かが作ったクラスやDLLはどの文字列の型を採用しているかはわかりません。あらゆる型から、目的の型に変換できないと、ひとつの型に統一して文字列を操作するということができないので、必須の知識になると思います。そもそもcharやWCHARやTCHARの違いとは何だったのか?覚えていますか?charはマルチバイト文字列、WCHARはワイド文字列、TCHARはプロジェクトの設定に従うマルチバイトあるいはワイド文字列でした。さらに復習しておくと、マルチバイトってのは全角文字を扱うときUnicodeではなくJISコードを拡張した日本語JISコードであり、Windowsならばマイクロソフトが提供しているCP932という文字コードセットで英数字は1バイト、全角文字は2バイトで表される文字、一方、ワイド文字ってのは、UnicodeでUTF-16という方式で英数、日本語は常に2バイトで表現される文字列です(あまり使わない諸外国の文字では4バイトになることもありますが…)。扱う文字コードの違いにより、変数に格納された数値の扱いが異なるため、それぞれの型は動作が異なるため、単純に代入するだけではだめです。だったら最初から代入するような表現だけで変換できるような仕組みを作ってくれれば良かったのですが、そうもいかないようです。そして、ここまで述べてきた型にマイクロソフトが独自に提供しているMFCで定義しているCString(マルチバイトのCStringA、ワイドのCStringW)からの変換や_bstr_t、CComBSTR、basic_string、System::Stringについても考えておきたいと思います。ほかにも独自に考えられた文字列型があれば、その独自の文字列型からの変換についても熟知する必要がありますが、独自の文字列型を提供している人や提供を受けた人が変換について考えれば良いので、ここでは有名どころだけを網羅することにします。マイクロソフトが提供している文字列型の詳細については以下のとおりです。

_bstr_t COM(Component Object Model アプリケーション同士のやりとりをするための技術)サポートクラス BSTR型の文字列クラス
http://msdn.microsoft.com/ja-jp/library/zthfhkd6.aspx
プリプロセッサ:comutil.h
ライブラリ:comsuppw.lib または comsuppwd.lib
BSTR型の解説:http://msdn.microsoft.com/ja-jp/library/ms221069.aspx
特徴:文字列長を常に保有し続けるBSTR型文字列のための文字列クラス

CComBSTR ATL(Active Template Library MFCの小型版と思っていいと思います。)として提供されるBSTR型の文字列クラス
http://msdn.microsoft.com/ja-jp/library/zh7x9w3f.aspx

basic_string 文字列テンプレートSTL(Standard Template Library ANSI標準テンプレートのライブラリ。)
http://msdn.microsoft.com/ja-jp/library/syxtdd4f.aspx

System::String C++/CLI(Common Language Infrastructure)の文字列型
http://msdn.microsoft.com/ja-jp/library/system.string(v=vs.110).aspx

MFC STL COM ATL CLIと短い略称ですが、それぞれは膨大なライブラリでして、その技術の応用についても、本当は知っていく必要がありますが、ActiveXやらDirectXやらと言い出したらきりがないほど情報技術は歴史が深いし、細かい。Webサービス、SOAP、ネットワーク、暗号化、セキュア…鬼のように沢山の既存技術がある。結構使われてる。ここで触れようとしていること自体もかなり多いけど、それでも、その中のほんの一部に過ぎない。

ちなみにワイド文字とマルチバイト文字の相互変換の簡単な例については
C 日本語文字列#マルチバイト文字列、ワイド文字列の相互変換
に記述があります。ここでは、更に踏み込んだ変換について触れたいと思います。

というわけで、これくらいできて当然という変換をやってみます。

#include <string>
#include <stdlib.h>
#include <iostream>
#include <tchar.h>
#include <mbstring.h>
#include "atlstr.h"//CString,CStringA,CStringWおよびLPTSTR,LPWSTR,LPSTR,LPCTSTR,LPCWSTR,LPCSTRの定義プリプロセッサ
#include "cstringt.h"//CStringTの定義プリプロセッサ
#include "atlbase.h"


//_bstr_t型を利用するプリプロセッサ
#include "comutil.h"
# ifdef _DEBUG
# pragma comment(lib, "comsuppwd.lib")
# else
# pragma comment(lib, "comsuppw.lib")
# endif


using namespace std;
using namespace System;
using namespace System::Runtime::InteropServices;

int main() {
  _tsetlocale(LC_ALL, _T("Japanese"));
  _locale_t locale;
  locale = _create_locale(LC_ALL, "Japanese");

  //プロジェクトの設定の文字セットでunicode文字セットを使用するに設定しているので、TCHAR→wchar_t型と定義される。
  //プロジェクトの設定をかえるとTCHAR→char型になるので、設定によってはTCHAR型に変換する方式を切り替えないとダメ。
  //実際の変換処理は#ifdef UNICODEで分けた関数を作る必要がある。
  
  size_t *sizeReturnValue;
  sizeReturnValue = new size_t;
  
  const char *pcStrMoto[] ={"char型の変数 配列要素1","char型の変数 配列要素2"}; 
  const LPSTR pLPSTRStrMoto[] = {"LPSTR型の変数 配列要素1","LPSTR型の変数 配列要素2"};
  TCHAR **pptcStrCnv = new TCHAR*[sizeof(pcStrMoto)/sizeof(*pcStrMoto)];
  LPTSTR *ppLPTSTRStrCnv = new TCHAR*[sizeof(pcStrMoto)/sizeof(*pcStrMoto)];
  
  _tprintf(_T("文字列の型変換char→TCHAR\n"));
  for(int i = 0; i < (sizeof(pcStrMoto)/sizeof(*pcStrMoto)); i++){

#ifdef UNICODE

	  //配列要素i番目の変換後のサイズ取得処理と要素iの実体化と文字列長さ設定。
	  mbstowcs_s(sizeReturnValue, NULL, 0,pcStrMoto[i], 0);
	  pptcStrCnv[i] = new TCHAR[*sizeReturnValue];
	  ppLPTSTRStrCnv[i] = new TCHAR[*sizeReturnValue];
	  //変換処理。
	  mbstowcs_s(sizeReturnValue, pptcStrCnv[i], *sizeReturnValue, pcStrMoto[i], *sizeReturnValue);
	  mbstowcs_s(sizeReturnValue, ppLPTSTRStrCnv[i], *sizeReturnValue, pcStrMoto[i], *sizeReturnValue);
#else

	  //マルチバイト定義の場合はchar型同志のコピーになるので変換不要。
	  pptcStrCnv[i] = new TCHAR[_tcslen(pcStrMoto[i]) + 1];
	  ppLPTSTRStrCnv[i] = new TCHAR[_tcslen(pcStrMoto[i]) + 1];
	  _tcscpy_s(pptcStrCnv[i],_tcslen(pcStrMoto[i]) + 1,pcStrMoto[i]);
	  _tcscpy_s(ppLPTSTRStrCnv[i],_tcslen(pcStrMoto[i]) + 1,pcStrMoto[i]);

#endif
	  //出力
	  _tprintf(_T("要素[%d]\n"),i);	  
	  for(unsigned int j = 0; j < *sizeReturnValue; j++){
		  _tprintf(_T("[%2d]=%x,"),j,*(*(pptcStrCnv + i) + j));
	  }
	  _tprintf(_T("\n%s\n"),*(pptcStrCnv + i));
  }

  _tprintf(_T("\n文字列の型変換TCHAR→char\n"));
  const TCHAR *ptcStrMoto[] = {_T("TCHAR型の変数 配列要素1"),_T("TCHAR型の変数 配列要素2")};
  const LPTSTR pLPTSTRStrMoto[] = {_T("LPTSTR型の変数 配列要素1"),_T("LPTSTR型の変数 配列要素2")};
  char **ppcStrCnv = new char*[sizeof(ptcStrMoto)/sizeof(*ptcStrMoto)];
  LPSTR *ppLPSTRStrCnv = new char*[sizeof(ptcStrMoto)/sizeof(*ptcStrMoto)];

  for(int i = 0; i < (sizeof(ptcStrMoto)/sizeof(*ptcStrMoto)); i++){

#ifdef UNICODE

	  //配列要素i番目の変換後のサイズ取得処理と要素iの実体化と文字列長さ設定。
	  wcstombs_s(sizeReturnValue, NULL, 0,ptcStrMoto[i], 0);
	  ppcStrCnv[i] = new char[*sizeReturnValue];
	  ppLPSTRStrCnv[i] = new char[*sizeReturnValue];
	  //変換処理。
	  wcstombs_s(sizeReturnValue, ppcStrCnv[i], *sizeReturnValue, ptcStrMoto[i], *sizeReturnValue);
	  wcstombs_s(sizeReturnValue, ppLPSTRStrCnv[i], *sizeReturnValue, ptcStrMoto[i], *sizeReturnValue);
#else

	  //マルチバイト定義の場合はchar型同志のコピーになるので変換不要。
	  ppcStrCnv[i] = new TCHAR[_tcslen(ptcStrMoto[i]) + 1];
	  ppLPSTRStrCnv[i] = new TCHAR[_tcslen(ptcStrMoto[i]) + 1];
	  _tcscpy_s(ppcStrCnv[i],_tcslen(ptcStrMoto[i]) + 1,ptcStrMoto[i]);
	  _tcscpy_s(ppLPSTRStrCnv[i],_tcslen(ptcStrMoto[i]) + 1,ptcStrMoto[i]);

#endif
	  //出力
	  _tprintf(_T("要素[%d]\n"),i);	  
	  for(unsigned int j = 0; j < *sizeReturnValue; j++){
		  printf("[%2d]=%x,",j,*(*(ppcStrCnv + i) + j));
		  printf("[%2d]=%x,",j,*(*(ppLPSTRStrCnv + i) + j));
	  }
	  printf("\n%s\n",*(ppcStrCnv + i));
	  printf("%s\n",*(ppLPSTRStrCnv + i));
  }

  //_bstr_t,CComBSTR,CString,System::Stringいずれもcharの先頭アドレスを代入すれば格納できる。
  _tprintf(_T("\n文字列の型変換char→_bstr_t\n"));
  _bstr_t bstrt[(sizeof(pcStrMoto)/sizeof(*pcStrMoto))];
  for(int i = 0; i < (sizeof(pcStrMoto)/sizeof(*pcStrMoto)); i++){
	bstrt[i] = pcStrMoto[i];//代入するだけ
	std::cout << bstrt[i] << std::endl;
	printf("_bstr_t %s\n",(LPCSTR)bstrt[i]);
  }

  _tprintf(_T("\n文字列の型変換char→CComBSTR\n"));
  CComBSTR ccombstr[(sizeof(pcStrMoto)/sizeof(*pcStrMoto))];
  for(int i = 0; i < (sizeof(pcStrMoto)/sizeof(*pcStrMoto)); i++){
	ccombstr[i] = pcStrMoto[i];//代入するだけ
	//マクロにより出力用の変数に文字列を移す必要がある。これはCComBSTR型の決まり。
	CW2A printstr(ccombstr[i]);
	std::cout << printstr << std::endl;
	printf("CComBSTR %s\n",printstr);
  }

  _tprintf(_T("\n文字列の型変換char→CStringA\n"));
  CStringA cstringa[(sizeof(pcStrMoto)/sizeof(*pcStrMoto))];
  for(int i = 0; i < (sizeof(pcStrMoto)/sizeof(*pcStrMoto)); i++){
	cstringa[i] = pcStrMoto[i];//代入するだけ
	std::cout << cstringa[i] << std::endl;
	printf("CStringA %s\n",cstringa[i]);
  }

  _tprintf(_T("\n文字列の型変換char→CStringW\n"));
  CStringW cstringw[(sizeof(pcStrMoto)/sizeof(*pcStrMoto))];
  for(int i = 0; i < (sizeof(pcStrMoto)/sizeof(*pcStrMoto)); i++){
	cstringw[i] = pcStrMoto[i];//代入するだけ
	std::wcout << (LPCTSTR)cstringw[i] << std::endl;
	_tprintf(_T("CStringW %s\n"),(LPCTSTR)cstringw[i]);
  }

  _tprintf(_T("\n文字列の型変換char→string\n"));
  string basicstring[(sizeof(pcStrMoto)/sizeof(*pcStrMoto))];
  for(int i = 0; i < (sizeof(pcStrMoto)/sizeof(*pcStrMoto)); i++){
	basicstring[i] = pcStrMoto[i];//代入するだけ
	cout << basicstring[i] << endl;
	printf("string %s\n",basicstring[i].c_str());
  }

  _tprintf(_T("\n文字列の型変換char→SystemString\n"));
  
  array<String^> ^systemstring = gcnew array<String^>(sizeof(pcStrMoto)/sizeof(*pcStrMoto));
  for(int i = 0; i < (sizeof(pcStrMoto)/sizeof(*pcStrMoto)); i++){
	char* pcStrTemp = new char[strlen(pcStrMoto[i]) + 1];
	strcpy_s(pcStrTemp,strlen(pcStrMoto[i]) + 1,pcStrMoto[i]);
	System::IntPtr ptr(pcStrTemp);//この関数がconst charの変換に対応していないので、上の2行でconst無しの変数に移し替えた。
	systemstring[i] = Marshal::PtrToStringAnsi(ptr);//IntPtrポインタを引数として変換するメソッド。
	Console::WriteLine("{0}", systemstring[i]);
	delete[] pcStrTemp;
  }
}

上から
char(LPSTR)→TCHAR(LPTSTR)
TCHAR(LPTSTR)→char(LPSTR)
の変換ここでは#ifndef UNICODEディレクティブによりTCHARの扱いをプロジェクトでどのように定義したか?によって動作が切り替わるように処理を組み込んでいます。以前に記述したサンプルC 日本語文字列#マルチバイト文字列、ワイド文字列の相互変換では、そこまでは言及していませんでした。プロジェクトの設定の文字セットでunicode文字セットを使用するに設定していれば、上側の#ifndef UNICODEから#elseまでに囲まれた部分が処理される。変換が必要かどうか?確認するのがよいです。
その次に
charから_bstr_tやCComBSTRやCStringA、CStringWのような変換について記述しています。このあたりの変数はchar型のポインタを代入するだけで変換をしてくれます。但し、出力するときには少し工夫が必要になります。これはどちらかというと、独自の文字列クラスの仕様によるものなので、それぞれの文字クラスについて学習を進める必要があります。先に示したリンクからそれぞれのクラスについて学習してみて下さい。こちらのサイトでもいつかは触れていきたいですが、まだまだ先になりそうです。
それから、
charからstring、System::Stringへの変換について記述しています。
System::Stringは共通言語ランタイムですので、動作させるにはプロジェクトの設定を変更しないといけません。
しかも、設定を変更しても従来のC言語の記法と共通言語ランタイムの記法を混同させるとVisual C++のコードインテリジェンス機能が完全停止しますのでコード編集が大変になりますので、このサンプルのような混同したプリプロセッサ定義をして無理やり、いろいろな変換のサンプルを記述するのは良くないです。

IntelliSenseOff.png

上記の図のようなエラーになります。共通言語ランタイムとプリプロセッサの混同に関する詳細な条件もまた後程調査ですかね。ほんとに調べるのかは定かでありませんが…

※コードインテリセンスは、例えばにstrcpyのようなよく使う関数を使うときに引数には何を定義すればよいのか、ヒントを与える機能です strcpy( までタイプすると第一引数には char* 型のコピー先の変数、第二引数にはコピー元のchar* 型の変数、第三引数にはコピーする文字の配列の長さを指定するべきだとポップアップ表示がされます。クラス変数や構造体変数を使う場合にも 変数名. あるいは変数名-> までタイプするとメンバ変数や、メソッドのリストが表示されます。メソッドの引数のヒントも関数と同様にポップアップ表示されます。

複数の文字列プリプロセッサを混同しないのが本来のやりかたになります。System::Stringへのchar変数の代入だけは複数の処理ステップが必要になります。System::String用に準備されているメソッドがconst char*の引数に対応していなかったり、独自の仕組みでしか引数をとることができないからです。ちなみに、文字列の配列でなければ、もう少し簡単に変換ができて、
System::String ^systemstring = gcnew String(char*型変数);
のように記述するだけで、コンストラクタの初期値によって変換自体は完了させることができます。と、ここまでchar型からの変換だけで、ずいぶんと長い説明になってしまいました。このあたりの配列を使った場合の文字列変換についてまとめている文書は意外と少ないと思います。次は逆変換ですかね。こっちのほうが大変なんだよね。そしたら次はTCHARからの変換か…で、その逆変換。ふむー。先は長い。普段こんなに変換ばっかりやんないので、この記事キツイっすね。なんでこんなん書いてるんやろ。役に立つんか?ほんまに…

マネージコードを利用するために実施した設定変更
C/C++
全般
 デバッグ情報の形式      プログラムデータベース /Zi ← エディットコンティニュのプログラムデータベース /ZI
 共通言語ランタイム サポート 共通言語ランタイムサポート /clr ←共通言語ランタイムをサポートしない
コード生成
 最小リビルドを有効にする。 いいえ /Gm- ←はい /Gm
 C++の例外を有効にする    はい SEHの例外あり /EHa ← はい /EHsc
 基本ランタイムチェック   規定 ← 両方 /RTC1

VisualStudioのコードインテリセンスを有効にしたままC++とC++/CLI(共通言語)の勉強したい場合は、C++/CLIプロジェクトでもC++言語は使えるので、プロジェクトの作成からやりなおしてみると良いです。


更に変換を続けたのものが以下になります。

	_tsetlocale(LC_ALL, _T("Japanese"));
	_locale_t locale;
	locale = _create_locale(LC_ALL, "Japanese");

	const char *pcStrMoto[] ={"char型の変数 配列要素1","char型の変数 配列要素2"};
	const LPSTR pLPSTRStrMoto[] = {"LPSTR型の変数 配列要素1","LPSTR型の変数 配列要素2"};

	const TCHAR *ptcStrMoto[] = {_T("TCHAR型の変数 要素1"),_T("TCHAR型の変数 要素2")};
	const LPTSTR pLPTSTRStrMoto[] = {_T("LPTSTR型の変数 要素1"),_T("LPTSTR型の変数 要素2")};

	size_t *sizeReturnValue;
	sizeReturnValue = new size_t;

	int *pnStrArrayElement;
	pnStrArrayElement = new int;

	*pnStrArrayElement = sizeof(ptcStrMoto)/sizeof(*ptcStrMoto);

	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・TCHAR→char変換
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("文字列の型変換TCHAR→char\n"));

	char **ppcStrCnv = new char*[*pnStrArrayElement];//要素数分の大きさ+1を確保
	LPSTR *ppLPSTRStrCnv = new char*[*pnStrArrayElement];//要素数分の大きさ+1を確保

#ifdef UNICODE
	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		
		wcstombs_s(sizeReturnValue, NULL, 0,ptcStrMoto[i], 0);
		
		ppcStrCnv[i] = new char[*sizeReturnValue];
		ppLPSTRStrCnv[i] = new char[*sizeReturnValue];

		wcstombs_s(sizeReturnValue, ppcStrCnv[i], *sizeReturnValue, ptcStrMoto[i], *sizeReturnValue);
		wcstombs_s(sizeReturnValue, ppLPSTRStrCnv[i], *sizeReturnValue, ptcStrMoto[i], *sizeReturnValue);
		
	}
#else
	//無変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		
		ppcStrCnv[i] = new char[*sizeReturnValue];
		ppLPSTRStrCnv[i] = new char[*sizeReturnValue];

		_tcscpy_s(ppcStrCnv[i], _tcslen(ptcStrMoto[i]) + 1, ptcStrMoto[i]);
		_tcscpy_s(ppLPSTRStrCnv[i], _tcslen(ptcStrMoto[i]) + 1, ptcStrMoto[i]);
		
	}
#endif
	//char出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		printf("%s\n",*(ppcStrCnv + i));
	}
	//LPSTR出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		printf("%s\n",*(ppLPSTRStrCnv + i));
	}

	printf("%d\n", sizeof(**ppcStrCnv));
	printf("%d\n", sizeof(*ppcStrCnv));
	printf("%d\n", sizeof(ppcStrCnv));//動的に確保した配列の大きさは取得できない。

	printf("%d\n", sizeof(**ptcStrMoto));
	printf("%d\n", sizeof(*ptcStrMoto));
	printf("%d\n", sizeof(ptcStrMoto));

	//動的確保変数の解放
	for(int i = 0;i < *pnStrArrayElement ;i++){
		delete[] *(ppcStrCnv + i);
		delete[] *(ppLPSTRStrCnv + i);
	}
	delete[] ppcStrCnv;
	delete[] ppLPSTRStrCnv;

	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・_bstr_t→char
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換_bstr_t→char\n"));
	
	const _bstr_t bstrtOrigin[] = {_T("_bstr_t型の変数 配列要素1"),_T("_bstr_t型の変数 配列要素2"),_T("_bstr_t型の変数 配列要素3")};

	printf("%d\n", sizeof(*bstrtOrigin));
	printf("%d\n", sizeof(bstrtOrigin));

	*pnStrArrayElement = sizeof(bstrtOrigin)/sizeof(*bstrtOrigin);
	
	ppcStrCnv = new char*[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){

		*sizeReturnValue = strlen((char*)bstrtOrigin[i]) + 1;

		ppcStrCnv[i] = new char[*sizeReturnValue];

		strcpy_s(ppcStrCnv[i], *sizeReturnValue, (char*)bstrtOrigin[i]);
	}
	
	//char出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		printf("%s\n",*(ppcStrCnv + i));
	}

	//動的確保変数の解放
	for(int i = 0;i < *pnStrArrayElement ;i++){
		delete[] *(ppcStrCnv + i);
	}	
	delete[] ppcStrCnv;


	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・_bstr_t→TCHAR
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換_bstr_t→TCHAR\n"));
	
	TCHAR **pptcStrCnv = new TCHAR*[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){

		*sizeReturnValue = _tcslen((TCHAR*)bstrtOrigin[i]) + 1;

		pptcStrCnv[i] = new TCHAR[*sizeReturnValue];

		_tcscpy_s(pptcStrCnv[i], *sizeReturnValue, (TCHAR*)bstrtOrigin[i]);
	}
	
	//char出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		_tprintf(_T("%s\n"),*(pptcStrCnv + i));
	}

	//動的確保変数の解放
	for(int i = 0;i < *pnStrArrayElement ;i++){
		delete[] *(pptcStrCnv + i);
	}
	delete[] pptcStrCnv;

	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・_bstr_t→CComBSTR(マルチバイト文字ワイド文字両対応 プロジェクト設定に従う)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換_bstr_t→CComBSTR\n"));
	
	CComBSTR *pccombstr_StrCnv = new CComBSTR[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		pccombstr_StrCnv[i] = (char*)bstrtOrigin[i];
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//char変換出力
		CW2A printstr(*(pccombstr_StrCnv + i));
		printf("%s\n",printstr);
		//無変換出力
		_tprintf(_T("%s\n"),*(pccombstr_StrCnv + i));
	}

	//動的確保変数の解放
	delete[] pccombstr_StrCnv;

	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・_bstr_t→CStringA(マルチバイト文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換_bstr_t→CStringA\n"));
	
	CStringA *pcstringa_StrCnv = new CStringA[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		pcstringa_StrCnv[i] = (char*)bstrtOrigin[i];
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//char変換出力
		printf("%s\n",pcstringa_StrCnv[i]);
	}

	//動的確保変数の解放
	delete[] pcstringa_StrCnv;

	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・_bstr_t→CStringW(ワイド文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換_bstr_t→CStringW\n"));
	
	CStringW *pcstringw_StrCnv = new CStringW[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		pcstringw_StrCnv[i] = (char*)bstrtOrigin[i];
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//wchar_t出力
		wprintf(L"%s\n",pcstringw_StrCnv[i]);
	}

	//動的確保変数の解放
	delete[] pcstringw_StrCnv;

	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・_bstr_t→string(マルチバイト文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換_bstr_t→string\n"));
	
	string *pstring_StrCnv = new string[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		pstring_StrCnv[i] = (char*)bstrtOrigin[i];
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//char変換出力
		printf("%s\n",pstring_StrCnv[i].c_str());
	}

	//動的確保変数の解放
	delete[] pstring_StrCnv;


	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・_bstr_t→SystemString(マルチバイト文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換_bstr_t→SystemString\n"));
	
	array<String^> ^psysstring_StrCnv = gcnew array<String^>(*pnStrArrayElement);
	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		char* pcStrTemp = new char[strlen((char*)bstrtOrigin[i]) + 1];
		strcpy_s(pcStrTemp,strlen((char*)bstrtOrigin[i]) + 1,(char*)bstrtOrigin[i]);

		System::IntPtr ptr(pcStrTemp);
		psysstring_StrCnv[i] = Marshal::PtrToStringAnsi(ptr);
		delete[] pcStrTemp;
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//char変換出力
		printf("%s\n",psysstring_StrCnv[i]);
		//TCHAR変換出力
#ifdef UNICODE
		_tprintf(_T("%s\n"),PtrToStringChars(psysstring_StrCnv[i]));
#else
		_tprintf(_T("%s\n"),psysstring_StrCnv[i]);
#endif
	}

	//動的確保変数の解放
	delete[] psysstring_StrCnv;



	delete[] sizeReturnValue;
	delete[] pnStrArrayElement;

_bstr_t型からの変換に関するサンプルを追加しました。

型変換とは関係ない部分になりますが、配列のサイズ確保処理について補足しておきますと、文字列配列の大きさは動的に生成した文字列配列からは取得することはできません。動的に生成したときの大きさをプログラマ自身が管理する必要があります。そのことを知ることができるのが上記の64行目のprintf文です。ここで動的に生成した文字列配列の大きさをsizeof関数で表示していますが、配列の大きさは32bitアプリならば、要素数2つなので4byte*2[配列要素数]=8byte(4byte=32bitでひとつのアドレス ※8bit=1byte)になるはずですが、4byteになっています。これはポインタ変数の大きさ、アドレス記憶変数ひとつ分の大きさになっていて、実際の配列サイズが返ってこないことが確認できます。

話は戻って…

上記のように変換を進めていて気づくことですが、それぞれの型は取り扱える文字コード長が異なります。_bstr_tやCComBSTRはマルチバイト文字とワイド文字の両方に対応していて、プロジェクト設定にしたがって記憶する文字型を切り替えています。CStringAはマルチバイト文字、CStringWはワイド文字です。stringとSystem::Stringはマルチバイト文字を扱うようになっています。したがって、変換時にはマルチバイト文字、ワイド文字変換が必要になるケースと必要ないケース。あるいは、プロジェクトの設定によって決まるケースがあるわけです。そのことから、_bstr_tやCComBSTを利用しておくという、ある程度、文字列の世界を熟知している人だけが選ぶ道があるようにも思えるわけです。マイクロソフトの用意したものを使うにしても賢い選択というのが、その時代、時代によって存在することになります。

開発するプログラムの枠組みによってはマルチバイトやワイド文字といった切り替えは気にしないという人もいるのかもしれません。具体的にどのように変換処理を記述するかについても、そのときそのときで違ってくることもあるのかもしれませんね。

変換の種類がたくさんありますが、wchar_tとcharの変換をおぼえれば、それぞれの特殊な型をchar型に変換する手法とcharからそれぞれの型に変換する手法をマスターすれば、変換手順は長くなる可能性があるものの、とにかくすべてを網羅できるわけです。特殊な型から特殊な型へのダイレクトな変換は覚えなくてもなんとかなる。覚えた方が楽になる。そんな感じです。それぞれの人の考え方にあわせて、覚える量を調節すれば済む話です。

マイクロソフトの説明書にも簡単な変換に関する文書があります。 http://msdn.microsoft.com/ja-jp/library/ms235631(v=vs.110).aspx

ここに記述している内容とさほど差はありません。わかりやすい方を参考にしてみて下さい。

では引き続き、変換のサンプルを…

	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・CComBSTR→char
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換CComBSTR→char\n"));
	
	const CComBSTR ccombstrOrigin[] = {_T("CComBSTR型の変数 配列要素1"),_T("CComBSTR型の変数 配列要素2"),_T("CComBSTR型の変数 配列要素3")};

	printf("%d\n", sizeof(*ccombstrOrigin));
	printf("%d\n", sizeof(ccombstrOrigin));

	*pnStrArrayElement = sizeof(ccombstrOrigin)/sizeof(*ccombstrOrigin);
	
	ppcStrCnv = new char*[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		CW2A strtemp(ccombstrOrigin[i]);
		*sizeReturnValue = strlen(strtemp) + 1;

		ppcStrCnv[i] = new char[*sizeReturnValue];

		strcpy_s(ppcStrCnv[i], *sizeReturnValue, strtemp);
	}
	
	//char出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		printf("%s\n",*(ppcStrCnv + i));
	}

	//動的確保変数の解放
	for(int i = 0;i < *pnStrArrayElement ;i++){
		delete[] *(ppcStrCnv + i);
	}	
	delete[] ppcStrCnv;

	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・CComBSTR→TCHAR
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換CComBSTR→TCHAR\n"));
	
	pptcStrCnv = new TCHAR*[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){

#ifdef UNICODE
		*sizeReturnValue = _tcslen(ccombstrOrigin[i]) + 1;
		pptcStrCnv[i] = new TCHAR[*sizeReturnValue];
		_tcscpy_s(pptcStrCnv[i], *sizeReturnValue, ccombstrOrigin[i]);
#else
		CW2A strtemp(ccombstrOrigin[i]);
		*sizeReturnValue = strlen(strtemp) + 1;
		pptcStrCnv[i] = new TCHAR[*sizeReturnValue];
		_tcscpy_s(pptcStrCnv[i], *sizeReturnValue, strtemp);
#endif
	}
	
	//char出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		_tprintf(_T("%s\n"),*(pptcStrCnv + i));
	}

	//動的確保変数の解放
	for(int i = 0;i < *pnStrArrayElement ;i++){
		delete[] *(pptcStrCnv + i);
	}
	delete[] pptcStrCnv;
	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・CComBSTR→_bstr_t(マルチバイト文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換_bstr_t→CComBSTR\n"));
	
	_bstr_t *pbstrt_StrCnv = new _bstr_t[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		CW2A strtemp(ccombstrOrigin[i]);
		pbstrt_StrCnv[i] = strtemp;
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//無変換出力
		printf("%s\n",(char*)pbstrt_StrCnv[i]);
		_tprintf(_T("%s\n"),(wchar_t*)pbstrt_StrCnv[i]);
	}

	//動的確保変数の解放
	delete[] pbstrt_StrCnv;
	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・CComBSTR→CStringA(マルチバイト文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換CComBSTR→CStringA\n"));
	
	pcstringa_StrCnv = new CStringA[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		CW2A strtemp(ccombstrOrigin[i]);
		pcstringa_StrCnv[i] = strtemp;
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//char変換出力
		printf("%s\n",pcstringa_StrCnv[i]);
	}

	//動的確保変数の解放
	delete[] pcstringa_StrCnv;

	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・CComBSTR→CStringW(ワイド文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換CComBSTR→CStringW\n"));
	
	pcstringw_StrCnv = new CStringW[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		CW2A strtemp(ccombstrOrigin[i]);
		pcstringw_StrCnv[i] = strtemp;
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//wchar_t出力
		wprintf(L"%s\n",pcstringw_StrCnv[i]);
	}

	//動的確保変数の解放
	delete[] pcstringw_StrCnv;
	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・CComBSTR→string(マルチバイト文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換CComBSTR→string\n"));
	
	pstring_StrCnv = new string[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		CW2A strtemp(ccombstrOrigin[i]);
		pstring_StrCnv[i] = strtemp;
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//char変換出力
		printf("%s\n",pstring_StrCnv[i].c_str());
	}

	//動的確保変数の解放
	delete[] pstring_StrCnv;

	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・CComBSTR→SystemString(マルチバイト文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換CComBSTR→SystemString\n"));
	
	psysstring_StrCnv = gcnew array<String^>(*pnStrArrayElement);
	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		CW2A strtemp(ccombstrOrigin[i]);

		System::IntPtr ptr(strtemp);
		psysstring_StrCnv[i] = Marshal::PtrToStringAnsi(ptr);
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//char変換出力
		printf("%s\n",psysstring_StrCnv[i]);
		//TCHAR変換出力
#ifdef UNICODE
		_tprintf(_T("%s\n"),PtrToStringChars(psysstring_StrCnv[i]));
#else
		_tprintf(_T("%s\n"),psysstring_StrCnv[i]);
#endif
	}

	//動的確保変数の解放
	delete[] psysstring_StrCnv;

引き続き、変換のサンプルを記述していきます。残るはCComBSTR、CStringA、CStringW、string、System::String基準の変換ですね。

	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・CStringA→char
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換CStringA→char\n"));
	
	const CStringA cstringaOrigin[] = {_T("CStringA型の変数 配列要素1"),_T("CStringA型の変数 配列要素2"),_T("CStringA型の変数 配列要素3")};

	printf("%d\n", sizeof(*cstringaOrigin));
	printf("%d\n", sizeof(cstringaOrigin));

	*pnStrArrayElement = sizeof(cstringaOrigin)/sizeof(*cstringaOrigin);
	
	ppcStrCnv = new char*[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		
		*sizeReturnValue = strlen(cstringaOrigin[i]) + 1;

		ppcStrCnv[i] = new char[*sizeReturnValue];

		strcpy_s(ppcStrCnv[i], *sizeReturnValue, cstringaOrigin[i]);
	}
	
	//char出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		printf("%s\n",*(ppcStrCnv + i));
	}

	//動的確保変数の解放
	for(int i = 0;i < *pnStrArrayElement ;i++){
		delete[] *(ppcStrCnv + i);
	}	
	delete[] ppcStrCnv;

	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・CStringA→TCHAR
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換CStringA→TCHAR\n"));
	
	pptcStrCnv = new TCHAR*[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){

#ifdef UNICODE
		mbstowcs_s(sizeReturnValue, NULL, 0,cstringaOrigin[i], 0);
		pptcStrCnv[i] = new TCHAR[*sizeReturnValue];
		mbstowcs_s(sizeReturnValue, pptcStrCnv[i], *sizeReturnValue,cstringaOrigin[i], *sizeReturnValue);
#else
		*sizeReturnValue = strlen(cstringaOrigin[i]) + 1;
		pptcStrCnv[i] = new TCHAR[*sizeReturnValue];
		_tcscpy_s(pptcStrCnv[i], *sizeReturnValue, cstringaOrigin[i]);
#endif
	}
	
	//char出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		_tprintf(_T("%s\n"),*(pptcStrCnv + i));
	}

	//動的確保変数の解放
	for(int i = 0;i < *pnStrArrayElement ;i++){
		delete[] *(pptcStrCnv + i);
	}
	delete[] pptcStrCnv;
	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・CStringA→_bstr_t(マルチバイト文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換CStringA→CComBSTR\n"));
	
	pbstrt_StrCnv = new _bstr_t[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		pbstrt_StrCnv[i] = cstringaOrigin[i];
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//無変換出力
		printf("%s\n",(char*)pbstrt_StrCnv[i]);
		_tprintf(_T("%s\n"),(wchar_t*)pbstrt_StrCnv[i]);
	}

	//動的確保変数の解放
	delete[] pbstrt_StrCnv;
	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・CComBSTR→CStringA(マルチバイト文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換CComBSTR→CStringA\n"));
	
	pcstringa_StrCnv = new CStringA[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		CW2A strtemp(ccombstrOrigin[i]);
		pcstringa_StrCnv[i] = strtemp;
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//char変換出力
		printf("%s\n",pcstringa_StrCnv[i]);
	}

	//動的確保変数の解放
	delete[] pcstringa_StrCnv;

	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・CStringA→CStringW(ワイド文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換CStringA→CStringW\n"));
	
	pcstringw_StrCnv = new CStringW[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		pcstringw_StrCnv[i] = cstringaOrigin[i];
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//wchar_t出力
		wprintf(L"%s\n",pcstringw_StrCnv[i]);
	}

	//動的確保変数の解放
	delete[] pcstringw_StrCnv;
	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・CStringA→string(マルチバイト文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換CStringA→string\n"));
	
	pstring_StrCnv = new string[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		pstring_StrCnv[i] = cstringaOrigin[i];
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//char変換出力
		printf("%s\n",pstring_StrCnv[i].c_str());
	}

	//動的確保変数の解放
	delete[] pstring_StrCnv;

	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・CStringA→SystemString(マルチバイト文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換CStringA→SystemString\n"));
	
	psysstring_StrCnv = gcnew array<String^>(*pnStrArrayElement);
	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		char* pcStrTemp = new char[strlen(cstringaOrigin[i]) + 1];
		strcpy_s(pcStrTemp,strlen(cstringaOrigin[i]) + 1,cstringaOrigin[i]);
		System::IntPtr ptr(pcStrTemp);
		psysstring_StrCnv[i] = Marshal::PtrToStringAnsi(ptr);
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//char変換出力
		printf("%s\n",psysstring_StrCnv[i]);
		//TCHAR変換出力
#ifdef UNICODE
		_tprintf(_T("%s\n"),PtrToStringChars(psysstring_StrCnv[i]));
#else
		_tprintf(_T("%s\n"),psysstring_StrCnv[i]);
#endif
	}

	//動的確保変数の解放
	delete[] psysstring_StrCnv;
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・CStringW→char
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換CStringW→char\n"));
	
	const CStringW cstringwOrigin[] = {_T("CStringW型の変数 配列要素1"),_T("CStringW型の変数 配列要素2"),_T("CStringW型の変数 配列要素3")};

	printf("%d\n", sizeof(*cstringwOrigin));
	printf("%d\n", sizeof(cstringwOrigin));

	*pnStrArrayElement = sizeof(cstringwOrigin)/sizeof(*cstringwOrigin);
	
	ppcStrCnv = new char*[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		
		wcstombs_s(sizeReturnValue, NULL, 0,cstringwOrigin[i], 0);
		ppcStrCnv[i] = new char[*sizeReturnValue];
		wcstombs_s(sizeReturnValue, ppcStrCnv[i], *sizeReturnValue,cstringwOrigin[i], *sizeReturnValue);
	}
	
	//char出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		printf("%s\n",*(ppcStrCnv + i));
	}

	//動的確保変数の解放
	for(int i = 0;i < *pnStrArrayElement ;i++){
		delete[] *(ppcStrCnv + i);
	}	
	delete[] ppcStrCnv;

	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・CStringW→TCHAR
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換CStringW→TCHAR\n"));
	
	pptcStrCnv = new TCHAR*[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){

#ifdef UNICODE
		*sizeReturnValue = _tcslen(cstringwOrigin[i]) + 1;
		pptcStrCnv[i] = new TCHAR[*sizeReturnValue];
		_tcscpy_s(pptcStrCnv[i], *sizeReturnValue, cstringwOrigin[i]);
#else
		wcstombs_s(sizeReturnValue, NULL, 0,cstringwOrigin[i], 0);
		ppcStrCnv[i] = new char[*sizeReturnValue];
		wcstombs_s(sizeReturnValue, ppcStrCnv[i], *sizeReturnValue,cstringwOrigin[i], *sizeReturnValue);
#endif
	}
	
	//char出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		_tprintf(_T("%s\n"),*(pptcStrCnv + i));
	}

	//動的確保変数の解放
	for(int i = 0;i < *pnStrArrayElement ;i++){
		delete[] *(pptcStrCnv + i);
	}
	delete[] pptcStrCnv;
	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・CStringW→_bstr_t(マルチバイト文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換CStringA→CComBSTR\n"));
	
	pbstrt_StrCnv = new _bstr_t[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		pbstrt_StrCnv[i] = cstringwOrigin[i];
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//無変換出力
		printf("%s\n",(char*)pbstrt_StrCnv[i]);
		_tprintf(_T("%s\n"),(wchar_t*)pbstrt_StrCnv[i]);
	}

	//動的確保変数の解放
	delete[] pbstrt_StrCnv;
	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・CStringW→CStringA(マルチバイト文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換CStringW→CStringA\n"));
	
	pcstringa_StrCnv = new CStringA[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		pcstringa_StrCnv[i] = cstringwOrigin[i];
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//char変換出力
		printf("%s\n",pcstringa_StrCnv[i]);
	}

	//動的確保変数の解放
	delete[] pcstringa_StrCnv;
	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・CStringA→CStringW(ワイド文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換CStringA→CStringW\n"));
	
	pcstringw_StrCnv = new CStringW[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		pcstringw_StrCnv[i] = cstringaOrigin[i];
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//wchar_t出力
		wprintf(L"%s\n",pcstringw_StrCnv[i]);
	}

	//動的確保変数の解放
	delete[] pcstringw_StrCnv;
	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・CStringW→string(マルチバイト文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換CStringW→string\n"));
	
	pstring_StrCnv = new string[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		wcstombs_s(sizeReturnValue, NULL, 0,cstringwOrigin[i], 0);
		char* pcStrTemp = new char[*sizeReturnValue];
		wcstombs_s(sizeReturnValue, pcStrTemp, *sizeReturnValue,cstringwOrigin[i], *sizeReturnValue);
		pstring_StrCnv[i] = pcStrTemp;
		delete[] pcStrTemp;
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//char変換出力
		printf("%s\n",pstring_StrCnv[i].c_str());
	}

	//動的確保変数の解放
	delete[] pstring_StrCnv;

	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・CStringW→SystemString(マルチバイト文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換CStringW→SystemString\n"));
	
	psysstring_StrCnv = gcnew array<String^>(*pnStrArrayElement);
	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		wcstombs_s(sizeReturnValue, NULL, 0,cstringwOrigin[i], 0);
		char* pcStrTemp = new char[*sizeReturnValue];
		wcstombs_s(sizeReturnValue, pcStrTemp, *sizeReturnValue,cstringwOrigin[i], *sizeReturnValue);
		System::IntPtr ptr(pcStrTemp);
		psysstring_StrCnv[i] = Marshal::PtrToStringAnsi(ptr);
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//char変換出力
		printf("%s\n",psysstring_StrCnv[i]);
		//TCHAR変換出力
#ifdef UNICODE
		_tprintf(_T("%s\n"),PtrToStringChars(psysstring_StrCnv[i]));
#else
		_tprintf(_T("%s\n"),psysstring_StrCnv[i]);
#endif
	}

	//動的確保変数の解放
	delete[] psysstring_StrCnv;
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・string→char
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換string→char\n"));
	
	const string stringOrigin[] = {"string型の変数 配列要素1","string型の変数 配列要素2","string型の変数 配列要素3"};

	printf("%d\n", sizeof(*stringOrigin));
	printf("%d\n", sizeof(stringOrigin));

	*pnStrArrayElement = sizeof(stringOrigin)/sizeof(*stringOrigin);
	
	ppcStrCnv = new char*[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		*sizeReturnValue = strlen(stringOrigin[i].c_str()) + 1;
		ppcStrCnv[i] = new char[*sizeReturnValue];
		strcpy_s(ppcStrCnv[i], *sizeReturnValue, stringOrigin[i].c_str());
	}
	
	//char出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		printf("%s\n",*(ppcStrCnv + i));
	}

	//動的確保変数の解放
	for(int i = 0;i < *pnStrArrayElement ;i++){
		delete[] *(ppcStrCnv + i);
	}	
	delete[] ppcStrCnv;

	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・string→TCHAR
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換string→TCHAR\n"));
	
	pptcStrCnv = new TCHAR*[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){

#ifdef UNICODE
		mbstowcs_s(sizeReturnValue, NULL, 0,stringOrigin[i].c_str(), 0);
		pptcStrCnv[i] = new TCHAR[*sizeReturnValue];
		mbstowcs_s(sizeReturnValue, pptcStrCnv[i], *sizeReturnValue,stringOrigin[i].c_str(), *sizeReturnValue);
#else
		*sizeReturnValue = strlen(stringOrigin[i].c_str()) + 1;
		pptcStrCnv[i] = new TCHAR[*sizeReturnValue];
		_tcscpy_s(pptcStrCnv[i], *sizeReturnValue, stringOrigin[i].c_str());
#endif
	}
	
	//char出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		_tprintf(_T("%s\n"),*(pptcStrCnv + i));
	}

	//動的確保変数の解放
	for(int i = 0;i < *pnStrArrayElement ;i++){
		delete[] *(pptcStrCnv + i);
	}
	delete[] pptcStrCnv;
	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・string→_bstr_t(マルチバイト文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換string→_bstr_t\n"));
	
	pbstrt_StrCnv = new _bstr_t[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		pbstrt_StrCnv[i] = stringOrigin[i].c_str();
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//無変換出力
		printf("%s\n",(char*)pbstrt_StrCnv[i]);
		_tprintf(_T("%s\n"),(wchar_t*)pbstrt_StrCnv[i]);
	}

	//動的確保変数の解放
	delete[] pbstrt_StrCnv;

	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・string→CComBSTR(マルチバイト文字ワイド文字両対応 プロジェクト設定に従う)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換string→CComBSTR\n"));
	
	pccombstr_StrCnv = new CComBSTR[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		pccombstr_StrCnv[i] = stringOrigin[i].c_str();
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//char変換出力
		CW2A printstr(*(pccombstr_StrCnv + i));
		printf("%s\n",printstr);
		//無変換出力
		_tprintf(_T("%s\n"),*(pccombstr_StrCnv + i));
	}

	//動的確保変数の解放
	delete[] pccombstr_StrCnv;

	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・string→CStringA(マルチバイト文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換string→CStringA\n"));
	
	pcstringa_StrCnv = new CStringA[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		pcstringa_StrCnv[i] = stringOrigin[i].c_str();
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//char変換出力
		printf("%s\n",pcstringa_StrCnv[i]);
	}

	//動的確保変数の解放
	delete[] pcstringa_StrCnv;
	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・string→CStringW(ワイド文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換string→CStringW\n"));
	
	pcstringw_StrCnv = new CStringW[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		pcstringw_StrCnv[i] = stringOrigin[i].c_str();
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//wchar_t出力
		wprintf(L"%s\n",pcstringw_StrCnv[i]);
	}

	//動的確保変数の解放
	delete[] pcstringw_StrCnv;
	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・string→SystemString(マルチバイト文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換string→SystemString\n"));
	
	psysstring_StrCnv = gcnew array<String^>(*pnStrArrayElement);
	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		char* pcStrTemp = new char[strlen(stringOrigin[i].c_str()) + 1];
		strcpy_s(pcStrTemp,strlen(stringOrigin[i].c_str()) + 1,stringOrigin[i].c_str());
		System::IntPtr ptr(pcStrTemp);
		psysstring_StrCnv[i] = Marshal::PtrToStringAnsi(ptr);
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//char変換出力
		printf("%s\n",psysstring_StrCnv[i]);
		//TCHAR変換出力
#ifdef UNICODE
		_tprintf(_T("%s\n"),PtrToStringChars(psysstring_StrCnv[i]));
#else
		_tprintf(_T("%s\n"),psysstring_StrCnv[i]);
#endif
	}

	//動的確保変数の解放
	delete[] psysstring_StrCnv;
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・SystemString→char
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換string→char\n"));
	
	array<String^> ^sysstringOrigin = gcnew array<String^>{"SystemString型の変数 配列要素1","SystemString型の変数 配列要素2","SystemString型の変数 配列要素3"};

	printf("%d\n", sysstringOrigin->Length);
	
	*pnStrArrayElement = sysstringOrigin->Length;
	
	ppcStrCnv = new char*[*pnStrArrayElement];//要素数分の大きさ+1を確保
	
	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		pin_ptr<const wchar_t> wch = PtrToStringChars(sysstringOrigin[i]);
		wcstombs_s(sizeReturnValue, NULL, 0,wch, 0);
		ppcStrCnv[i] = new char[*sizeReturnValue];
		wcstombs_s(sizeReturnValue, ppcStrCnv[i], *sizeReturnValue, wch, *sizeReturnValue);
	}
	
	//char出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		printf("%s\n",*(ppcStrCnv + i));
	}

	//動的確保変数の解放
	for(int i = 0;i < *pnStrArrayElement ;i++){
		delete[] *(ppcStrCnv + i);
	}	
	delete[] ppcStrCnv;

	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・SystemString→TCHAR
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換SystemString→TCHAR\n"));
	
	pptcStrCnv = new TCHAR*[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){

#ifdef UNICODE
		pin_ptr<const wchar_t> wch = PtrToStringChars(sysstringOrigin[i]);
		*sizeReturnValue = _tcslen(wch) + 1;
		pptcStrCnv[i] = new TCHAR[*sizeReturnValue];
		_tcscpy_s(pptcStrCnv[i], *sizeReturnValue, wch);
#else
		pin_ptr<const wchar_t> wch = PtrToStringChars(sysstringOrigin[i]);
		wcstombs_s(sizeReturnValue, NULL, 0,wch, 0);
		pptcStrCnv[i] = new TCHAR[*sizeReturnValue];
		wcstombs_s(sizeReturnValue, pptcStrCnv[i], *sizeReturnValue, wch, *sizeReturnValue);
#endif
	}
	
	//char出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		_tprintf(_T("%s\n"),*(pptcStrCnv + i));
	}

	//動的確保変数の解放
	for(int i = 0;i < *pnStrArrayElement ;i++){
		delete[] *(pptcStrCnv + i);
	}
	delete[] pptcStrCnv;
	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・SystemString→_bstr_t(マルチバイト文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換SystemString→_bstr_t\n"));
	
	pbstrt_StrCnv = new _bstr_t[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		pin_ptr<const wchar_t> wch = PtrToStringChars(sysstringOrigin[i]);
		wcstombs_s(sizeReturnValue, NULL, 0,wch, 0);
		char* pcStrTemp = new char[*sizeReturnValue];
		wcstombs_s(sizeReturnValue, pcStrTemp, *sizeReturnValue, wch, *sizeReturnValue);
		pbstrt_StrCnv[i] = pcStrTemp;
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//無変換出力
		printf("%s\n",(char*)pbstrt_StrCnv[i]);
		_tprintf(_T("%s\n"),(wchar_t*)pbstrt_StrCnv[i]);
	}

	//動的確保変数の解放
	delete[] pbstrt_StrCnv;
	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・SystemString→CComBSTR(マルチバイト文字ワイド文字両対応 プロジェクト設定に従う)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換SystemString→CComBSTR\n"));
	
	pccombstr_StrCnv = new CComBSTR[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		pin_ptr<const wchar_t> wch = PtrToStringChars(sysstringOrigin[i]);
		wcstombs_s(sizeReturnValue, NULL, 0,wch, 0);
		char* pcStrTemp = new char[*sizeReturnValue];
		wcstombs_s(sizeReturnValue, pcStrTemp, *sizeReturnValue, wch, *sizeReturnValue);
		pccombstr_StrCnv[i] = pcStrTemp;
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//char変換出力
		CW2A printstr(*(pccombstr_StrCnv + i));
		printf("%s\n",printstr);
		//無変換出力
		_tprintf(_T("%s\n"),*(pccombstr_StrCnv + i));
	}

	//動的確保変数の解放
	delete[] pccombstr_StrCnv;
	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・SystemString→CStringA(マルチバイト文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換SystemString→CStringA\n"));
	
	pcstringa_StrCnv = new CStringA[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		pin_ptr<const wchar_t> wch = PtrToStringChars(sysstringOrigin[i]);
		wcstombs_s(sizeReturnValue, NULL, 0,wch, 0);
		char* pcStrTemp = new char[*sizeReturnValue];
		wcstombs_s(sizeReturnValue, pcStrTemp, *sizeReturnValue, wch, *sizeReturnValue);
		pcstringa_StrCnv[i] = pcStrTemp;
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//char変換出力
		printf("%s\n",pcstringa_StrCnv[i]);
	}

	//動的確保変数の解放
	delete[] pcstringa_StrCnv;
	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・SystemString→CStringW(ワイド文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換SystemString→CStringW\n"));
	
	pcstringw_StrCnv = new CStringW[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		pin_ptr<const wchar_t> wch = PtrToStringChars(sysstringOrigin[i]);
		wcstombs_s(sizeReturnValue, NULL, 0,wch, 0);
		char* pcStrTemp = new char[*sizeReturnValue];
		wcstombs_s(sizeReturnValue, pcStrTemp, *sizeReturnValue, wch, *sizeReturnValue);
		pcstringw_StrCnv[i] = pcStrTemp;
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//wchar_t出力
		wprintf(L"%s\n",pcstringw_StrCnv[i]);
	}

	//動的確保変数の解放
	delete[] pcstringw_StrCnv;
	
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
	//・SystemString→string(マルチバイト文字)
	//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

	_tprintf(_T("\n文字列の型変換SystemString→string\n"));
	
	pstring_StrCnv = new string[*pnStrArrayElement];//要素数分の大きさ+1を確保

	//変換
	for(int i = 0; i < *pnStrArrayElement; i++){
		pin_ptr<const wchar_t> wch = PtrToStringChars(sysstringOrigin[i]);
		wcstombs_s(sizeReturnValue, NULL, 0,wch, 0);
		char* pcStrTemp = new char[*sizeReturnValue];
		wcstombs_s(sizeReturnValue, pcStrTemp, *sizeReturnValue, wch, *sizeReturnValue);
		pstring_StrCnv[i] = pcStrTemp;
	}
	
	//出力
	for(int i = 0; i < *pnStrArrayElement; i++){
		//char変換出力
		printf("%s\n",pstring_StrCnv[i].c_str());
	}

	//動的確保変数の解放
	delete[] pstring_StrCnv;

と、こんな感じです。もっと効率の良い変換があったらごめんなさい。動的に必要最小限のメモリを確保させながらの処理だとこんな感じになるよっていう例になっていると思います。プログラムによっては、配列の大きさを固定したっていいわけです。その方がコーディングや変数管理が楽になる場合もあります。この章に関しては以上です。既に準備されている型の変換なんてのは、変換するための関数が準備されているので、比較的簡単な技術になります。全角半角変換や文字コード変換処理を実際に組む方が難しいかもしれません。それもおいおい記述したいと思います。

※2014/09/21 ここまでで、疲れたから、また暫くオヤスミ。次に気が向くのはいつになるのか、誰も知らない。

文字列文字コード変換

文字コード変換は、標準関数だけでは実現できない変換になっています。自分で変換関数を作るのもありですが、既にきちんと作られた変換ライブラリが無償で配布されています。変換処理自体を作成するには、文字コードの体系の理解やエンコーディングの理解が必要です。そして、それに対応する変換処理を作るわけです。ShiftJIS CP932とUnicode UTF-16はマルチバイトとワイド文字の変換によって実現できますので、UTF-8やEUCあるいはJISといった変換には変換処理の作成が必要になってきます。更に同じUnicodeでもUTF-16やUTF-32ではリトルエンディアン方式、ビッグエンディアン方式さらにはBOM(ByteOrderMark)の付加といった変換を考慮する必要もあります。UTF-16やUTF-32では常に複数Byteで一文字が表現され、その複数バイト塊のメモリアドレスの番地の小さい方から文字コードの上位バイトの情報をいれるビッグエンディアンと番地の小さい方に下位バイトの情報をいれていくリトルエンディアンがあり、その文字コードファイルの先頭2ByteにBOMという符号で、どっちの方式が使われているのかを示すものです。先頭アドレスからFF FE と格納されていればビッグエンディアン、FE FF と格納されていればリトルエンディアンであることを示します。あえてBOMを付与しないで、プログラムがどっちの文字コードが使われているか解析する方法や文書を閲覧する都度、ユーザが選択する方法もあります。UTF-8では、複数バイト使われるときには徐々にバイト数が大きくなる可変長の方式をとっていて、リトルエンディアンとかビッグエンディアンという概念がないため、BOMは不要ですが、付与するパターンもゆるされており、BOMありで統一してテキストを作成している人もいることから無意味なBOM付きファイルも存在します。また、このBOMが原因でファイルの読み取りに失敗してしまうUTF-8採用のプログラムも多いようです。どっちのファイルも読み取れるプログラムを作るのが良いですが、BOMありでは動作しないものと割りきって、BOM付きUTF-8を切り捨てていくプログラム作成者もいるように思います。こうすることでBOM付きUTF-8を撲滅することになっているのかもしれません。自分もUTF-8にはBOMは不要だと感じています。あえてBOMを付けれる出力のサポートも必要だと思うし、読み取れるようにもしときたいものですね。そんなプログラム作ったことないですけど…。


ちなみにVisualStudioのコード編集エディタはデフォルトでは、ShiftJIS CP932が利用されます。変更するには、メニューのファイル(F)-保存オプションの詳細設定のエンコード欄の値を変えることで実現できます。行の終わり欄で改行コードも変更できます。特別にテキストの中で特定の文字コードセットのリテラルを使う必要がある以外は基本はShiftJIS CP932でいいのではないでしょうか?L""リテラルでUnicodeも使えますし。


こういった文字コードの変換は、自分のプログラムだけで動作するのではなく、外部におかれた情報を読み込む、あるいは書き換えるといった動作をする場合に必要になってきます。固定の外部ファイルとのやりとりであれば、変換が発生しないように構築すると必要ないし、変換が必要であっても1種類の変換で済みます。ありとあらゆる場所からテキストを読むこむようなプログラムになると、この文字コード変換を網羅しないと利便性が損なわれる結果になります。文字コードの掃出し処理が間違っていると、文字コード破壊プログラムになってしまう可能性もありますので、自分で変換処理を作って、書き出すプログラムを作るのはかなりの知識を必要とします。実績のある変換処理ライブラリを使った方が安全です。速度改善の必要があるような人は自分で変換処理作りに挑むのもよいのかもしれません。ここでは、既存の変換処理ライブラリを使う方法を示し、時間があれば具体的な変換処理の作成について触れたいと思います。


例によって、全部のサンプルを記事に書き起こすまでに相当な時間がかかると思いますので、すこしづつ進めていくことになると思います。


C++/CLIだとEncodingというライブラリを使うことで変換ができますが、C++以下の範囲で扱う場合はIBM社が作成したオープンソースICU(International Components for Unicode)、GNUプロジェクトによって作られLGPLのICONVというライブラリがあります。ほかにも有志によって作られたフリーソフトnkf.dllというライブラリを使う方法もあります。ここではICUを使った方法について記述します。GNUは自由に使うことのできるUnixとしてのOSであるLinuxの開発プロジェクトで、GNU's Not Unixという意味だそうです。そうすると先頭にまたGNUが出てくるから、それは何の意味?ってなるとまたGNUはGNU's Not Unixって略語だよっていう風になる再帰的な略語です。GNUのGの意味は思考を停止しない限り永遠に再帰されます。LGPL(Lesser General Public License)は簡単に表現するとコンパイルするプログラムの配布には関与しないライセンスですが、ライブラリそのものや、制限に関するライセンスの詳細な意味についてはGNUプロジェクトによる説明で解釈して下さい。


ICUはC++(Cpp)の技術を含むプロジェクトですので、C言語の領域からは外れます。C言語だけで、文字コード変換をやろうとすると、このようなライブラリがないために、もっと基礎的なコード変換技術を身に着ける必要があります。根本的な言語でやる方が敷居が高いというのはアセンブラ含めて、同様の事だと言えると思います。ここではそのような基礎的な技術は紹介しないので、C言語だけで実現する必要がある場合には、ICUプロジェクトのソースを解析するなりして、クラスやC++で登場する構文をを使わない形式で実現できるように挑戦してみると良いです。とても大変なことだと思います。


ICU導入

ICUのWebSite
http://site.icu-project.org/
まずここから最新のソースファイルをダウンロードします。
執筆時点'14/09ではICU Versionは5.4で、VisualStudio2012を使っている分には、現在のICU4CのソースはCygwinも必要とせずにビルドができます。VisualStudio2010で作られているので、初回のソューションファイルオープンでプロジェクトの変換が必要になります。ビルド後VisualStudioの設定も以下のとおり実施する必要があります。ICUが利用している開発環境よりも新しいものなら楽にビルドできそうです。


  • ビルドの手順
1.環境変数PATHに[icu配置パス]\icu\bin を追加。x64プロジェクトをビルドするときは[icu配置パス]\icu\bin64っすかね。
2.[icu配置パス]\icu\source\allinone\allinone.sln を開く(プロジェクト変換ダイアログ表示されたらOKで変換する)
注)icu配置パスを決めてからビルドをしないと駄目です。ビルド時に生成されるファイルで絶対パスで管理されるDATAがあるため、ビルド後に移動させると動かなくなります。
3.メニューの[ビルド]-[バッチビルド]を選択し、表示されたダイアログで、Win32かx64のプロジェクトを選択してから→[ビルド]で、ひたすら待つ。全部でおそらく15分(Corei7 3610QM 2.3GHz)くらい
次のプロジェクト設定Win32 or x64をビルドするときには手順1に戻ることになります。
4.コマンドプロンプトでテストプログラムを動かす。テストコマンドの引数4種類全部確かめるなら以下4つのコマンド全部実行。それか自分の目的のテストだけ実施。あるいはテストはやらないのもひとつの選択肢。
  • [icu配置パス]\icu\source\allinone\allinone.bat x86 Debug
  • [icu配置パス]\icu\source\allinone\allinone.bat x86 Release
  • [icu配置パス]\icu\source\allinone\allinone.bat x64 Debug
  • [icu配置パス]\icu\source\allinone\allinone.bat x64 Release


ソースを見ることができるのですが、とにかく巨大なプロジェクトで、ソースを見るのも勉強になるかもしれません。巨大プロジェクトならではのアイデア満載かなぁと思います。ワーニングもいっぱいだけど、まぁ気にしない。バグもあるみたいで、同時に配布されるテストプログラムをビルド後に動作させてみるのですが、うまく動かない関数もありました。今回の目的にはあまり関係してこない部分のバグだと思うので、このまま使ってみます。


以下、テスト時のエラーログです。ひょっとしたら自分だけなのかもしれませんが、既知のバグみたいな?いやテストだけで出るエラーみたいな感じなので気にしないことにします。え?ホントにそれでいいのか?調べるのも面倒だし、放置。

KNOWN ISSUES
#11217 <http://bugs.icu-project.org/trac/ticket/11217>
  /tsutil/cldrtest/TestExemplarSet
     "Fix result of uscript_getCode for yi: USCRIPT_YI -> USCRIPT_HEBREW"
  /tsutil/cldrtest/VerifyTranslation
     "Fix result of uscript_getCode for yi: USCRIPT_YI -> USCRIPT_HEBREW"
(To run suppressed tests, use the -K option.)


SUMMARY:
******* [Total error count:     590]
 Errors in
[/tsconv/nucnvtst/TestISO_2022_JP]
[/tsconv/nucnvtst/TestJIS]
[/tsconv/nucnvtst/TestISO_2022_JP_1]
[/tsconv/nucnvtst/TestISO_2022_JP_2]
[/tsconv/nucnvtst/TestHZ]
[/tsconv/nucnvtst/TestJitterbug2346]
[/tsconv/nccbtst/TestSkipCallBack]
[/tsconv/nccbtst/TestSubWithValueCallBack]
[/tsconv/ncnvtst/TestResetBehaviour]

表:icucheck.bat Win32 Release のテスト結果でのエラー


あらゆる文字コードの変換だけでなく、大文字小文字相互変換、全角半角相互変換、ひらがなカタカナ相互変換、他にもひらがなやカタカナからのローマ字相互変換といった複雑な変換も提供しています。そのほかにも通貨フォーマットや時間表記変換といった面白い変換も組み込まれています。ここでは文字コード変換のみを取り上げます。文字コードをファイルに出力するのはもっとあとで説明する予定なので、ここでは、サンプルとなる文字列をその文字コード体系に合わせて16進数で表現した配列として扱い、それを基に変換後の文字コード配列が生成されるのを確かめます。変換後の文字コードがUNICODEやShiftJIS以外になる場合も同じく16進数で表現される配列で示します、逆に異なる文字コードから基に戻す変換も確認します。


  • プロジェクトの設定
1.Win32プロジェクトなら[icu配置パス]\icu\bin、x64プロジェクトなら[icu配置パス]\icu\bin64という絶対パスを、プロジェクト毎にプロジェクトの設定の中(ALT+F7キーで表示されるダイアログの構成プロパティの中)にあるVCディレクトリの実行可能ファイルディレクトリにパスを設定する。Win32プロジェクトとx64プロジェクトとでパスは異なるためプロジェクト毎に毎回設定した方が良いでしょう。文字コード変換を必要とするプロジェクトを作成する都度、設定しましょう。面倒な人でWin32プロジェクトしか作らないX54しか作らないという人は、どちらかの環境変数PATHに入れてもいいでしょう。でも結局、この次にlibファイルやincludeファイルの設定もしないといけないし、やっぱりプロジェクト毎に設定しましょう。一見、面倒そうだけど、そんなにたくさんプロジェクト作る人ってあんまりいないと思います。小さなサンプルコードプロジェクトをたくさん作って、それで変換テストしまくる人は大変かもしれませんね。自分は基本Win32のプロジェクトで遊んでるので、環境変数PATHにWin32プロジェクト用の実行パスを登録しています。X64をビルドするときに、また切り替えます。このとき、新しいパス変数を作ってPATH32=[icu配置パス]\icu\bin;(他のSDKの実行パス);…;…でPATH64=[icu配置パス]\icu\bin64;(他のSDKの実行パス);…;…として、環境変数PATHにはPATH=(既存パス1);(既存パス2);%PATH32%;(既存パスn)みたいに登録するといろいろなSDKをまとめて切り替えられます。64に変えればいいんだもんね。
2.Win32プロジェクトなら[icu配置パス]\icu\lib、x64プロジェクトなら[icu配置パス]\icu\lib64という絶対パスをプロジェクト毎にプロジェクトの設定の中にあるVCディレクトリのライブラリディレクトリにパスを設定する。これも実行パス同様に管理して環境変数LIB32とLIB64を作って、環境変数LIB=%LIB32%とかにすると、プロジェクトの設定にはライブラリの欄に$(LIB)と入力するだけでよくなります。
3.Win32,x64共通ですが、[icu配置パス]\icu\includeをプロジェクト毎にプロジェクトの設定の中にあるVCディレクトリのインクルードディレクトリに設定します。自分はこれも環境変数INCLUDEを作っていて、$(INCLUDE)と設定しています。
4.デバッグプロジェクトならicuucd.lib、リリースプロジェクトならicuuc.libという追加するべきライブラリ名をプロジェクト毎にプロジェクトの設定の中にあるリンカー-入力の追加の依存ファイル欄に追記します。


このICUはC++/CLIプロジェクトのような/clrオプションコンパイルには対応していません。UConverterという構造体が実態が未定義のまま利用されていますが、このような宣言がされているだけのものをビルドするとリンカーエラーが発生し、プログラムは動作しません。対処方法はあると思いますが、ICUはとてつもなく大きなプロジェクトファイルです。C++/CLIで使っていくには、現在のバージョンを永続的に使っていくのか?メンテしきれるか?どうかも含めた検討が必要になります。


リンカー ツールの警告 LNK4248 http://msdn.microsoft.com/ja-jp/library/h8027ys9.aspx


プロジェクトの設定は以上です。以下が実際の変換サンプルになります。これまでのサンプルもそうですが、実際にはサポートクラスを作って、毎回新しく変数定義しているものは、クラスを呼ぶたびにまとめて生成されるようなセットを作るべきです。変換サポートクラスの作り方については、クラスに関する記述を掘り下げるセクションで触れたいと思いますが、ここでは例のごとく直線的なプログラムになっています。

#include "stdafx.h"
#include <unicode/ucnv.h>
#include <stdlib.h>
#include <string>
#include <mbstring.h>
#include <iostream>
#include <locale>
#include <tchar.h>
#include "atlstr.h"//CString,CStringA,CStringWおよびLPTSTR,LPWSTR,LPSTR,LPCTSTR,LPCWSTR,LPCSTRの定義プリプロセッサ
#include "cstringt.h"//CStringTの定義プリプロセッサ
#include "atlbase.h"
#include "comutil.h"
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
  _tsetlocale(LC_ALL, _T("Japanese"));



  // "JIS文字列" のJIS(iso-2022-jp)表現↓
  string jis = "\x4A\x49\x53\x1B\x24\x42\x4A\x38\x3B\x7A\x4E\x73\x1B\x28\x42";
  wstring wstr = L"JIS文字列";




  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  // JIS → UNICODE
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  UConverter* ucnvTest;
  UErrorCode uerrorNum;
  int nStrSize;
  int nStrResultSize;
  nStrSize = jis.size();

  std::wstring stringCnvResult(nStrSize, L'\0');

  ucnvTest = ucnv_open("iso-2022-jp", &uerrorNum);
  nStrResultSize = ucnv_toUChars(
    ucnvTest,
    &stringCnvResult[0],	stringCnvResult.size(),	// 変換先のポインタとサイズ
    &jis[0],				nStrSize,				// 変換元のポインタとサイズ
    &uerrorNum
  ); 
  stringCnvResult.resize(nStrResultSize);
  ucnv_close(ucnvTest);
  _tprintf(_T("%s\n"), stringCnvResult.c_str());	  
  

  wchar_t* pwcStr = new wchar_t[_tcslen(stringCnvResult.c_str()) + 1];
  _tcscpy_s(pwcStr,_tcslen(stringCnvResult.c_str()) + 1,stringCnvResult.c_str());
  for(int i = 0;i < (int)_tcslen(stringCnvResult.c_str());i++){
    printf("%04x:",*(pwcStr + i));
  }
    printf("\n");




  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  // UNICODE → JIS
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  ucnvTest = ucnv_open("iso-2022-jp", &uerrorNum);

  nStrSize = wstr.size();
  std::string stringCnvResult2(ucnv_getMaxCharSize(ucnvTest) * nStrSize , L'\0');


  nStrResultSize = ucnv_fromUChars(
    ucnvTest,
    &stringCnvResult2[0],	stringCnvResult2.size(),	// 変換先のポインタとサイズ
    &wstr[0],				nStrSize,					// 変換元のポインタとサイズ
    &uerrorNum
  ); 
  stringCnvResult2.resize(nStrResultSize);
  ucnv_close(ucnvTest);
  _tprintf(_T("%s\n"), stringCnvResult2.c_str());	  
	

  char* pcStr = new char[strlen(stringCnvResult2.c_str()) + 1];
  strcpy_s(pcStr,strlen(stringCnvResult2.c_str()) + 1,stringCnvResult2.c_str());
  for(int i = 0;i < (int)strlen(stringCnvResult2.c_str());i++){
    printf("%02x:",*(pcStr + i));
  }
    printf("\n");



  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  // UNICODE → SJIS
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  ucnvTest = ucnv_open("shift_jis", &uerrorNum);

  nStrSize = wstr.size();
  std::string stringCnvResult3(ucnv_getMaxCharSize(ucnvTest) * nStrSize , L'\0');


  nStrResultSize = ucnv_fromUChars(
    ucnvTest,
    &stringCnvResult3[0],	stringCnvResult3.size(),	// 変換先のポインタとサイズ
    &wstr[0],				nStrSize,					// 変換元のポインタとサイズ
    &uerrorNum
  ); 
  stringCnvResult3.resize(nStrResultSize);
  ucnv_close(ucnvTest);
  printf("%s\n", stringCnvResult3.c_str());	  
	

  char* pcStr2 = new char[strlen(stringCnvResult3.c_str()) + 1];
  strcpy_s(pcStr2,strlen(stringCnvResult3.c_str()) + 1,stringCnvResult3.c_str());
  for(int i = 0;i < (int)strlen(stringCnvResult3.c_str());i++){
    printf("%02x:",0x000000ff & *(pcStr2 + i));
  }
    printf("\n");


  return 0;
}

という具合に変換すると結果として

JIS文字列
004a:0049:0053:6587:5b57:5217:
????稻獎?B
4a:49:53:1b:24:42:4a:38:3b:7a:4e:73:1b:28:42:
JIS文字列
4a:49:53:95:b6:8e:9a:97:f1:

のようなものが得られます。


これですべての変換が出来そうな気分ですが、実際にはshift_jisコード上で展開される携帯電話各社の絵文字があったり、UNICODE上での文字の重複による変換先の最適化、UNICODEにしかない文字の変換があった場合の処理など実際のプログラミング技術が、ここをスタート地点として、広がっています。auとDocomoは絵文字を統一させたんだったかも…ソフトバンクはかなり独自の体系を維持。ウィルコム。イーモバ…ふむ、どうなってるんだろ。んで、UNICODEにも少し絵文字が追加されてたり…。でも結局、それを表示するのはその文字コードのグリフをもったフォントファイルなわけです。やれる範囲。結構、限られてます。特殊文字には、年号とかもあります。次の年号の文字コードとかも、後で変更追加になるに違いない。㍻。←平成を一文字で表した環境依存文字。文字コードの世界が今のようになった歴史を知るのも面白いですし、これからの文字コードはどうあるべきかを議論する国際的なワーキンググループも活動しています。絶賛迷走中あるいは理想形へ向かって進行中です。メールで半角カタカナが使えないのはJISコード iso-2022-jpのおかげです。更にはメールはBase64という変換方式もあり、バイナリーコードや文字列コードを先頭から6ビットずつ4セットにしていくエンコードがあります。こうやってメールはおくられているんだなと感心したものです。http://ja.wikipedia.org/wiki/Base64


上記のサンプルですが、動作しますが、ucnv_openでエラーコードが返ってきています。-122だそうで調べてみるとAMBIGUOUS_ALIAS_WARNING(-122) This converter alias can go to different converter implementations.とのことです。ワーニングレベルと認識できますが、他の方法で実装できるよだって。どういうこと?


対応している変換は以下の文字コードになります。一番左が基本文字セット名で、その横にエイリアス名の個数、★の横にスペース区切りで全エイリアス名(別名)を記載しました。ucnv_open関数などの第一引数にはこのエイリアス名を大文字小文字の区別なく記載することができるようです。


shift_jisは41行目と42行目でダブって登場しますね。ibm-943も。どっちが使われる?調べるの?いやいや、調べきれそうもないです。よくみかけるコード名でいくと上側が使われるべきなんでしょうけど。どっちもそれほど変わらないのか、それとも、これが俗にいうマイクロソフトsjisとsjisの違いなのか?Windowsなら上で、それ以外は純粋なShift_JISってことなのか。AMBIGUOUS_ALIAS_WARNINGの原因にはあいまいな変換がされたことに由来するらしいが、どうも、指定方法の問題ではないらしい。謎は謎のままに。謎というよりはICUの英語を理解できないだけだが…。だからどうしろとは言わない説明書がうらめしい。


ibm-943_P15A-2003 19 ★ibm-943_P15A-2003 ibm-943 Shift_JIS MS_Kanji csShiftJIS windows-31j csWindows31J x-sjis x-ms-cp932 cp932 windows-93cp943c IBM-943C ms932 pck sjis ibm-943_VSUB_VPUA x-MS932_0213 x-JISAutoDetect

ibm-943_P130-1999 7 ★ibm-943_P130-1999 ibm-943 Shift_JIS cp943 943 ibm-943_VASCII_VSUB_VPUA x-IBM943

UTF-8 14 ★UTF-8 ibm-1208 ibm-1209 ibm-5304 ibm-5305 ibm-13496 ibm-13497 ibm-17592 ibm-17593 windows-65001 cp1208 x-UTF_8J unicode-1-1-utf unicode-2-0-utf-8
UTF-16 7 ★UTF-16 ISO-10646-UCS-2 ibm-1204 ibm-1205 unicode csUnicode ucs-2
UTF-16BE 21 ★UTF-16BE x-utf-16be UnicodeBigUnmarked ibm-1200 ibm-1201 ibm-13488 ibm-13489 ibm-17584 ibm-17585 ibm-21680 ibm-21681 ibm-257 ibm-25777 ibm-29872 ibm-29873 ibm-61955 ibm-61956 windows-1201 cp1200 cp1201 UTF16_BigEndian
UTF-16LE 17 ★UTF-16LE x-utf-16le UnicodeLittleUnmarked ibm-1202 ibm-1203 ibm-13490 ibm-13491 ibm-17586 ibm-17587 ibm-21682 ibm-21683 ibm-778 ibm-25779 ibm-29874 ibm-29875 UTF16_LittleEndian windows-1200
UTF-32 6 ★UTF-32 ISO-10646-UCS-4 ibm-1236 ibm-1237 csUCS4 ucs-4
UTF-32BE 5 ★UTF-32BE UTF32_BigEndian ibm-1232 ibm-1233 ibm-9424
UTF-32LE 4 ★UTF-32LE UTF32_LittleEndian ibm-1234 ibm-1235
UTF16_PlatformEndian 1 ★UTF16_PlatformEndian
UTF16_OppositeEndian 1 ★UTF16_OppositeEndian
UTF32_PlatformEndian 1 ★UTF32_PlatformEndian
UTF32_OppositeEndian 1 ★UTF32_OppositeEndian
UTF-16BE,version=1 2 ★UTF-16BE,version=1 UnicodeBig
UTF-16LE,version=1 3 ★UTF-16LE,version=1 UnicodeLittle x-UTF-16LE-BOM
UTF-16,version=1 1 ★UTF-16,version=1
UTF-16,version=2 1 ★UTF-16,version=2
UTF-7 4 ★UTF-7 windows-65000 unicode-1-1-utf-7 unicode-2-0-utf-7
IMAP-mailbox-name 1 ★IMAP-mailbox-name
SCSU 3 ★SCSU ibm-1212 ibm-1213
BOCU-1 4 ★BOCU-1 csBOCU-1 ibm-1214 ibm-1215
CESU-8 2 ★CESU-8 ibm-9400
ISO-8859-1 11 ★ISO-8859-1 ibm-819 IBM819 cp819 latin1 8859_1 csISOLatin1 iso-ir-100 ISO_8859-1:1987 l1 819
US-ASCII 16 ★US-ASCII ASCII ANSI_X3.4-1968 ANSI_X3.4-1986 ISO_646.irv:1991 iso_646.irv:1983 ISO646-US us csASCII iso-ir-6 cp367 ascii7 64
windows-20127 ibm-367 IBM367
gb18030 4 ★gb18030 ibm-1392 windows-54936 GB18030
ibm-912_P100-1995 12 ★ibm-912_P100-1995 ibm-912 ISO-8859-2 ISO_8859-2:1987 latin2 csISOLatin2 iso-ir-101 l2 8859_2 cp912 912 windows-2859
ibm-913_P100-2000 12 ★ibm-913_P100-2000 ibm-913 ISO-8859-3 ISO_8859-3:1988 latin3 csISOLatin3 iso-ir-109 l3 8859_3 cp913 913 windows-2859
ibm-914_P100-1995 12 ★ibm-914_P100-1995 ibm-914 ISO-8859-4 latin4 csISOLatin4 iso-ir-110 ISO_8859-4:1988 l4 8859_4 cp914 914 windows-2859
ibm-915_P100-1995 11 ★ibm-915_P100-1995 ibm-915 ISO-8859-5 cyrillic csISOLatinCyrillic iso-ir-144 ISO_8859-5:1988 8859_5 cp915 915 window28595
ibm-1089_P100-1995 16 ★ibm-1089_P100-1995 ibm-1089 ISO-8859-6 arabic csISOLatinArabic iso-ir-127 ISO_8859-6:1987 ECMA-114 ASMO-708 8859_6p1089 1089 windows-28596 ISO-8859-6-I ISO-8859-6-E x-ISO-8859-6S
ibm-9005_X110-2007 13 ★ibm-9005_X110-2007 ibm-9005 ISO-8859-7 8859_7 greek greek8 ELOT_928 ECMA-118 csISOLatinGreek iso-ir-126 ISO_8859-7987 windows-28597 sun_eu_greek
ibm-813_P100-1995 4 ★ibm-813_P100-1995 ibm-813 cp813 813
ibm-5012_P100-1999 12 ★ibm-5012_P100-1999 ibm-5012 ISO-8859-8 hebrew csISOLatinHebrew iso-ir-138 ISO_8859-8:1988 ISO-8859-8-I ISO-8859-8-8859_8 windows-28598 hebrew8
ibm-916_P100-1995 4 ★ibm-916_P100-1995 ibm-916 cp916 916
ibm-920_P100-1995 15 ★ibm-920_P100-1995 ibm-920 ISO-8859-9 latin5 csISOLatin5 iso-ir-148 ISO_8859-9:1989 l5 8859_9 cp920 920 windows-2859ECMA-128 turkish8 turkish
iso-8859_10-1998 7 ★iso-8859_10-1998 ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6
iso-8859_11-2001 4 ★iso-8859_11-2001 ISO-8859-11 thai8 x-iso-8859-11
ibm-921_P100-1995 8 ★ibm-921_P100-1995 ibm-921 ISO-8859-13 8859_13 windows-28603 cp921 921 x-IBM921
iso-8859_14-1998 7 ★iso-8859_14-1998 ISO-8859-14 iso-ir-199 ISO_8859-14:1998 latin8 iso-celtic l8
ibm-923_P100-1998 13 ★ibm-923_P100-1998 ibm-923 ISO-8859-15 Latin-9 l9 8859_15 latin0 csisolatin0 csisolatin9 iso8859_15_fdis cp923 923 wdows-28605
ibm-942_P12A-1999 10 ★ibm-942_P12A-1999 ibm-942 ibm-932 cp932 shift_jis78 sjis78 ibm-942_VSUB_VPUA ibm-932_VSUB_VPUA x-IBM942 x-IBM942C
ibm-943_P15A-2003 19 ★ibm-943_P15A-2003 ibm-943 Shift_JIS MS_Kanji csShiftJIS windows-31j csWindows31J x-sjis x-ms-cp932 cp932 windows-93cp943c IBM-943C ms932 pck sjis ibm-943_VSUB_VPUA x-MS932_0213 x-JISAutoDetect
ibm-943_P130-1999 7 ★ibm-943_P130-1999 ibm-943 Shift_JIS cp943 943 ibm-943_VASCII_VSUB_VPUA x-IBM943
ibm-33722_P12A_P12A-2009_U2 5 ★ibm-33722_P12A_P12A-2009_U2 ibm-33722 ibm-5050 ibm-33722_VPUA IBM-eucJP
ibm-33722_P120-1999 9 ★ibm-33722_P120-1999 ibm-33722 ibm-5050 cp33722 33722 ibm-33722_VASCII_VPUA x-IBM33722 x-IBM33722A x-IBM33722C
ibm-954_P101-2007 4 ★ibm-954_P101-2007 ibm-954 x-IBM954 x-IBM954C
euc-jp-2007 7 ★euc-jp-2007 EUC-JP Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese X-EUC-JP eucjis ujis
ibm-1373_P100-2002 3 ★ibm-1373_P100-2002 ibm-1373 windows-950
windows-950-2000 7 ★windows-950-2000 Big5 csBig5 windows-950 x-windows-950 x-big5 ms950
ibm-950_P110-1999 5 ★ibm-950_P110-1999 ibm-950 cp950 950 x-IBM950
ibm-1375_P100-2008 5 ★ibm-1375_P100-2008 ibm-1375 Big5-HKSCS big5hk HKSCS-BIG5
ibm-5471_P100-2006 7 ★ibm-5471_P100-2006 ibm-5471 Big5-HKSCS MS950_HKSCS hkbig5 big5-hkscs:unicode3.0 x-MS950-HKSCS
ibm-1386_P100-2001 5 ★ibm-1386_P100-2001 ibm-1386 cp1386 windows-936 ibm-1386_VSUB_VPUA
windows-936-2000 5 ★windows-936-2000 GBK CP936 MS936 windows-936
ibm-1383_P110-1999 10 ★ibm-1383_P110-1999 ibm-1383 GB2312 csGB2312 cp1383 1383 EUC-CN ibm-eucCN hp15CN ibm-1383_VPUA
ibm-5478_P100-1995 8 ★ibm-5478_P100-1995 ibm-5478 GB_2312-80 chinese iso-ir-58 csISO58GB231280 gb2312-1980 GB2312.1980-0
euc-tw-2014 2 ★euc-tw-2014 EUC-TW
ibm-964_P110-1999 8 ★ibm-964_P110-1999 ibm-964 ibm-eucTW cns11643 cp964 964 ibm-964_VPUA x-IBM964
ibm-949_P110-1999 6 ★ibm-949_P110-1999 ibm-949 cp949 949 ibm-949_VASCII_VSUB_VPUA x-IBM949
ibm-949_P11A-1999 6 ★ibm-949_P11A-1999 ibm-949 cp949c ibm-949_VSUB_VPUA x-IBM949C IBM-949C
ibm-970_P110_P110-2006_U2 13 ★ibm-970_P110_P110-2006_U2 ibm-970 EUC-KR KS_C_5601-1987 windows-51949 csEUCKR ibm-eucKR KSC_5601 5601 cp97070 ibm-970_VPUA x-IBM970
ibm-971_P100-1995 4 ★ibm-971_P100-1995 ibm-971 ibm-971_VPUA x-IBM971
ibm-1363_P11B-1998 14 ★ibm-1363_P11B-1998 ibm-1363 KS_C_5601-1987 KS_C_5601-1989 KSC_5601 csKSC56011987 korean iso-ir-149 cp1363 5601 kscindows-949 ibm-1363_VSUB_VPUA x-IBM1363C
ibm-1363_P110-1997 4 ★ibm-1363_P110-1997 ibm-1363 ibm-1363_VASCII_VSUB_VPUA x-IBM1363
windows-949-2000 10 ★windows-949-2000 windows-949 KS_C_5601-1987 KS_C_5601-1989 KSC_5601 csKSC56011987 korean iso-ir-149 ms949 x-KSC5601
windows-874-2000 5 ★windows-874-2000 TIS-620 windows-874 MS874 x-windows-874
ibm-874_P100-1995 8 ★ibm-874_P100-1995 ibm-874 ibm-9066 cp874 TIS-620 tis620.2533 eucTH x-IBM874
ibm-1162_P100-1999 2 ★ibm-1162_P100-1999 ibm-1162
ibm-437_P100-1995 7 ★ibm-437_P100-1995 ibm-437 IBM437 cp437 437 csPC8CodePage437 windows-437
ibm-720_P100-1997 5 ★ibm-720_P100-1997 ibm-720 windows-720 DOS-720 x-IBM720
ibm-737_P100-1997 7 ★ibm-737_P100-1997 ibm-737 IBM737 cp737 windows-737 737 x-IBM737
ibm-775_P100-1996 7 ★ibm-775_P100-1996 ibm-775 IBM775 cp775 csPC775Baltic windows-775 775
ibm-850_P100-1995 7 ★ibm-850_P100-1995 ibm-850 IBM850 cp850 850 csPC850Multilingual windows-850
ibm-851_P100-1995 6 ★ibm-851_P100-1995 ibm-851 IBM851 cp851 851 csPC851
ibm-852_P100-1995 7 ★ibm-852_P100-1995 ibm-852 IBM852 cp852 852 csPCp852 windows-852
ibm-855_P100-1995 8 ★ibm-855_P100-1995 ibm-855 IBM855 cp855 855 csIBM855 csPCp855 windows-855
ibm-856_P100-1995 6 ★ibm-856_P100-1995 ibm-856 IBM856 cp856 856 x-IBM856
ibm-857_P100-1995 7 ★ibm-857_P100-1995 ibm-857 IBM857 cp857 857 csIBM857 windows-857
ibm-858_P100-1997 8 ★ibm-858_P100-1997 ibm-858 IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro cp858 windows-858
ibm-860_P100-1995 6 ★ibm-860_P100-1995 ibm-860 IBM860 cp860 860 csIBM860
ibm-861_P100-1995 8 ★ibm-861_P100-1995 ibm-861 IBM861 cp861 861 cp-is csIBM861 windows-861
ibm-862_P100-1995 8 ★ibm-862_P100-1995 ibm-862 IBM862 cp862 862 csPC862LatinHebrew DOS-862 windows-862
ibm-863_P100-1995 6 ★ibm-863_P100-1995 ibm-863 IBM863 cp863 863 csIBM863
ibm-864_X110-1999 5 ★ibm-864_X110-1999 ibm-864 IBM864 cp864 csIBM864
ibm-865_P100-1995 6 ★ibm-865_P100-1995 ibm-865 IBM865 cp865 865 csIBM865
ibm-866_P100-1995 7 ★ibm-866_P100-1995 ibm-866 IBM866 cp866 866 csIBM866 windows-866
ibm-867_P100-1998 3 ★ibm-867_P100-1998 ibm-867 x-IBM867
ibm-868_P100-1995 7 ★ibm-868_P100-1995 ibm-868 IBM868 CP868 868 csIBM868 cp-ar
ibm-869_P100-1995 8 ★ibm-869_P100-1995 ibm-869 IBM869 cp869 869 cp-gr csIBM869 windows-869
ibm-878_P100-1996 7 ★ibm-878_P100-1996 ibm-878 KOI8-R koi8 csKOI8R windows-20866 cp878
ibm-901_P100-1999 2 ★ibm-901_P100-1999 ibm-901
ibm-902_P100-1999 2 ★ibm-902_P100-1999 ibm-902
ibm-922_P100-1999 6 ★ibm-922_P100-1999 ibm-922 IBM922 cp922 922 x-IBM922
ibm-1168_P100-2002 4 ★ibm-1168_P100-2002 ibm-1168 KOI8-U windows-21866
ibm-4909_P100-1999 2 ★ibm-4909_P100-1999 ibm-4909
ibm-5346_P100-1998 4 ★ibm-5346_P100-1998 ibm-5346 windows-1250 cp1250
ibm-5347_P100-1998 5 ★ibm-5347_P100-1998 ibm-5347 windows-1251 cp1251 ANSI1251
ibm-5348_P100-1997 4 ★ibm-5348_P100-1997 ibm-5348 windows-1252 cp1252
ibm-5349_P100-1998 4 ★ibm-5349_P100-1998 ibm-5349 windows-1253 cp1253
ibm-5350_P100-1998 4 ★ibm-5350_P100-1998 ibm-5350 windows-1254 cp1254
ibm-9447_P100-2002 4 ★ibm-9447_P100-2002 ibm-9447 windows-1255 cp1255
ibm-9448_X100-2005 5 ★ibm-9448_X100-2005 ibm-9448 windows-1256 cp1256 x-windows-1256S
ibm-9449_P100-2002 4 ★ibm-9449_P100-2002 ibm-9449 windows-1257 cp1257
ibm-5354_P100-1998 4 ★ibm-5354_P100-1998 ibm-5354 windows-1258 cp1258
ibm-1250_P100-1995 3 ★ibm-1250_P100-1995 ibm-1250 windows-1250
ibm-1251_P100-1995 3 ★ibm-1251_P100-1995 ibm-1251 windows-1251
ibm-1252_P100-2000 3 ★ibm-1252_P100-2000 ibm-1252 windows-1252
ibm-1253_P100-1995 3 ★ibm-1253_P100-1995 ibm-1253 windows-1253
ibm-1254_P100-1995 3 ★ibm-1254_P100-1995 ibm-1254 windows-1254
ibm-1255_P100-1995 2 ★ibm-1255_P100-1995 ibm-1255
ibm-5351_P100-1998 3 ★ibm-5351_P100-1998 ibm-5351 windows-1255
ibm-1256_P110-1997 2 ★ibm-1256_P110-1997 ibm-1256
ibm-5352_P100-1998 3 ★ibm-5352_P100-1998 ibm-5352 windows-1256
ibm-1257_P100-1995 2 ★ibm-1257_P100-1995 ibm-1257
ibm-5353_P100-1998 3 ★ibm-5353_P100-1998 ibm-5353 windows-1257
ibm-1258_P100-1997 3 ★ibm-1258_P100-1997 ibm-1258 windows-1258
macos-0_2-10.2 7 ★macos-0_2-10.2 macintosh mac csMacintosh windows-10000 macroman x-macroman
macos-6_2-10.4 5 ★macos-6_2-10.4 x-mac-greek windows-10006 macgr x-MacGreek
macos-7_3-10.2 7 ★macos-7_3-10.2 x-mac-cyrillic windows-10007 mac-cyrillic maccy x-MacCyrillic x-MacUkraine
macos-29-10.2 7 ★macos-29-10.2 x-mac-centraleurroman windows-10029 x-mac-ce macce maccentraleurope x-MacCentralEurope
macos-35-10.2 5 ★macos-35-10.2 x-mac-turkish windows-10081 mactr x-MacTurkish
ibm-1051_P100-1995 7 ★ibm-1051_P100-1995 ibm-1051 hp-roman8 roman8 r8 csHPRoman8 x-roman8
ibm-1276_P100-1995 4 ★ibm-1276_P100-1995 ibm-1276 Adobe-Standard-Encoding csAdobeStandardEncoding
ibm-1006_P100-1995 6 ★ibm-1006_P100-1995 ibm-1006 IBM1006 cp1006 1006 x-IBM1006
ibm-1098_P100-1995 6 ★ibm-1098_P100-1995 ibm-1098 IBM1098 cp1098 1098 x-IBM1098
ibm-1124_P100-1996 5 ★ibm-1124_P100-1996 ibm-1124 cp1124 1124 x-IBM1124
ibm-1125_P100-1997 3 ★ibm-1125_P100-1997 ibm-1125 cp1125
ibm-1129_P100-1997 2 ★ibm-1129_P100-1997 ibm-1129
ibm-1131_P100-1997 3 ★ibm-1131_P100-1997 ibm-1131 cp1131
ibm-1133_P100-1997 2 ★ibm-1133_P100-1997 ibm-1133
ISO_2022,locale=ja,version=0 5 ★ISO_2022,locale=ja,version=0 ISO-2022-JP csISO2022JP x-windows-iso2022jp x-windows-50220
ISO_2022,locale=ja,version=1 7 ★ISO_2022,locale=ja,version=1 ISO-2022-JP-1 JIS_Encoding csJISEncoding ibm-5054 JIS x-windows-50221
ISO_2022,locale=ja,version=2 3 ★ISO_2022,locale=ja,version=2 ISO-2022-JP-2 csISO2022JP2
ISO_2022,locale=ja,version=3 2 ★ISO_2022,locale=ja,version=3 JIS7
ISO_2022,locale=ja,version=4 2 ★ISO_2022,locale=ja,version=4 JIS8
ISO_2022,locale=ko,version=0 3 ★ISO_2022,locale=ko,version=0 ISO-2022-KR csISO2022KR
ISO_2022,locale=ko,version=1 2 ★ISO_2022,locale=ko,version=1 ibm-25546
ISO_2022,locale=zh,version=0 4 ★ISO_2022,locale=zh,version=0 ISO-2022-CN csISO2022CN x-ISO-2022-CN-GB
ISO_2022,locale=zh,version=1 2 ★ISO_2022,locale=zh,version=1 ISO-2022-CN-EXT
ISO_2022,locale=zh,version=2 3 ★ISO_2022,locale=zh,version=2 ISO-2022-CN-CNS x-ISO-2022-CN-CNS
HZ 2 ★HZ HZ-GB-2312
x11-compound-text 3 ★x11-compound-text COMPOUND_TEXT x-compound-text
ISCII,version=0 6 ★ISCII,version=0 x-ISCII91 x-iscii-de windows-57002 iscii-dev ibm-4902
ISCII,version=1 6 ★ISCII,version=1 x-iscii-be windows-57003 iscii-bng windows-57006 x-iscii-as
ISCII,version=2 4 ★ISCII,version=2 x-iscii-pa windows-57011 iscii-gur
ISCII,version=3 4 ★ISCII,version=3 x-iscii-gu windows-57010 iscii-guj
ISCII,version=4 4 ★ISCII,version=4 x-iscii-or windows-57007 iscii-ori
ISCII,version=5 4 ★ISCII,version=5 x-iscii-ta windows-57004 iscii-tml
ISCII,version=6 4 ★ISCII,version=6 x-iscii-te windows-57005 iscii-tlg
ISCII,version=7 4 ★ISCII,version=7 x-iscii-ka windows-57008 iscii-knd
ISCII,version=8 4 ★ISCII,version=8 x-iscii-ma windows-57009 iscii-mlm
LMBCS-1 3 ★LMBCS-1 lmbcs ibm-65025
ibm-37_P100-1995 13 ★ibm-37_P100-1995 ibm-37 IBM037 ibm-037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl csIBM037 cp037 037 cpibm3cp37
ibm-273_P100-1995 7 ★ibm-273_P100-1995 ibm-273 IBM273 CP273 csIBM273 ebcdic-de 273
ibm-277_P100-1995 9 ★ibm-277_P100-1995 ibm-277 IBM277 cp277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 ebcdic-dk 277
ibm-278_P100-1995 9 ★ibm-278_P100-1995 ibm-278 IBM278 cp278 ebcdic-cp-fi ebcdic-cp-se csIBM278 ebcdic-sv 278
ibm-280_P100-1995 7 ★ibm-280_P100-1995 ibm-280 IBM280 CP280 ebcdic-cp-it csIBM280 280
ibm-284_P100-1995 8 ★ibm-284_P100-1995 ibm-284 IBM284 CP284 ebcdic-cp-es csIBM284 cpibm284 284
ibm-285_P100-1995 9 ★ibm-285_P100-1995 ibm-285 IBM285 CP285 ebcdic-cp-gb csIBM285 cpibm285 ebcdic-gb 285
ibm-290_P100-1995 6 ★ibm-290_P100-1995 ibm-290 IBM290 cp290 EBCDIC-JP-kana csIBM290
ibm-297_P100-1995 8 ★ibm-297_P100-1995 ibm-297 IBM297 cp297 ebcdic-cp-fr csIBM297 cpibm297 297
ibm-420_X120-1999 7 ★ibm-420_X120-1999 ibm-420 IBM420 cp420 ebcdic-cp-ar1 csIBM420 420
ibm-424_P100-1995 7 ★ibm-424_P100-1995 ibm-424 IBM424 cp424 ebcdic-cp-he csIBM424 424
ibm-500_P100-1995 8 ★ibm-500_P100-1995 ibm-500 IBM500 CP500 ebcdic-cp-be csIBM500 ebcdic-cp-ch 500
ibm-803_P100-1999 3 ★ibm-803_P100-1999 ibm-803 cp803
ibm-838_P100-1995 8 ★ibm-838_P100-1995 ibm-838 IBM838 IBM-Thai csIBMThai cp838 838 ibm-9030
ibm-870_P100-1995 7 ★ibm-870_P100-1995 ibm-870 IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870
ibm-871_P100-1995 8 ★ibm-871_P100-1995 ibm-871 IBM871 ebcdic-cp-is csIBM871 CP871 ebcdic-is 871
ibm-875_P100-1995 6 ★ibm-875_P100-1995 ibm-875 IBM875 cp875 875 x-IBM875
ibm-918_P100-1995 6 ★ibm-918_P100-1995 ibm-918 IBM918 CP918 ebcdic-cp-ar2 csIBM918
ibm-930_P120-1999 8 ★ibm-930_P120-1999 ibm-930 ibm-5026 IBM930 cp930 930 x-IBM930 x-IBM930A
ibm-933_P110-1995 5 ★ibm-933_P110-1995 ibm-933 cp933 933 x-IBM933
ibm-935_P110-1999 5 ★ibm-935_P110-1999 ibm-935 cp935 935 x-IBM935
ibm-937_P110-1999 5 ★ibm-937_P110-1999 ibm-937 cp937 937 x-IBM937
ibm-939_P120-1999 9 ★ibm-939_P120-1999 ibm-939 ibm-931 ibm-5035 IBM939 cp939 939 x-IBM939 x-IBM939A
ibm-1025_P100-1995 5 ★ibm-1025_P100-1995 ibm-1025 cp1025 1025 x-IBM1025
ibm-1026_P100-1995 6 ★ibm-1026_P100-1995 ibm-1026 IBM1026 CP1026 csIBM1026 1026
ibm-1047_P100-1995 5 ★ibm-1047_P100-1995 ibm-1047 IBM1047 cp1047 1047
ibm-1097_P100-1995 5 ★ibm-1097_P100-1995 ibm-1097 cp1097 1097 x-IBM1097
ibm-1112_P100-1995 5 ★ibm-1112_P100-1995 ibm-1112 cp1112 1112 x-IBM1112
ibm-1122_P100-1999 5 ★ibm-1122_P100-1999 ibm-1122 cp1122 1122 x-IBM1122
ibm-1123_P100-1995 5 ★ibm-1123_P100-1995 ibm-1123 cp1123 1123 x-IBM1123
ibm-1130_P100-1997 2 ★ibm-1130_P100-1997 ibm-1130
ibm-1132_P100-1998 2 ★ibm-1132_P100-1998 ibm-1132
ibm-1137_P100-1999 2 ★ibm-1137_P100-1999 ibm-1137
ibm-4517_P100-2005 2 ★ibm-4517_P100-2005 ibm-4517
ibm-1140_P100-1997 7 ★ibm-1140_P100-1997 ibm-1140 IBM01140 CCSID01140 CP01140 cp1140 ebcdic-us-37+euro
ibm-1141_P100-1997 7 ★ibm-1141_P100-1997 ibm-1141 IBM01141 CCSID01141 CP01141 cp1141 ebcdic-de-273+euro
ibm-1142_P100-1997 8 ★ibm-1142_P100-1997 ibm-1142 IBM01142 CCSID01142 CP01142 cp1142 ebcdic-dk-277+euro ebcdic-no-277+euro
ibm-1143_P100-1997 8 ★ibm-1143_P100-1997 ibm-1143 IBM01143 CCSID01143 CP01143 cp1143 ebcdic-fi-278+euro ebcdic-se-278+euro
ibm-1144_P100-1997 7 ★ibm-1144_P100-1997 ibm-1144 IBM01144 CCSID01144 CP01144 cp1144 ebcdic-it-280+euro
ibm-1145_P100-1997 7 ★ibm-1145_P100-1997 ibm-1145 IBM01145 CCSID01145 CP01145 cp1145 ebcdic-es-284+euro
ibm-1146_P100-1997 7 ★ibm-1146_P100-1997 ibm-1146 IBM01146 CCSID01146 CP01146 cp1146 ebcdic-gb-285+euro
ibm-1147_P100-1997 7 ★ibm-1147_P100-1997 ibm-1147 IBM01147 CCSID01147 CP01147 cp1147 ebcdic-fr-297+euro
ibm-1148_P100-1997 7 ★ibm-1148_P100-1997 ibm-1148 IBM01148 CCSID01148 CP01148 cp1148 ebcdic-international-500+euro
ibm-1149_P100-1997 7 ★ibm-1149_P100-1997 ibm-1149 IBM01149 CCSID01149 CP01149 cp1149 ebcdic-is-871+euro
ibm-1153_P100-1999 4 ★ibm-1153_P100-1999 ibm-1153 IBM1153 x-IBM1153
ibm-1154_P100-1999 2 ★ibm-1154_P100-1999 ibm-1154
ibm-1155_P100-1999 2 ★ibm-1155_P100-1999 ibm-1155
ibm-1156_P100-1999 2 ★ibm-1156_P100-1999 ibm-1156
ibm-1157_P100-1999 2 ★ibm-1157_P100-1999 ibm-1157
ibm-1158_P100-1999 2 ★ibm-1158_P100-1999 ibm-1158
ibm-1160_P100-1999 2 ★ibm-1160_P100-1999 ibm-1160
ibm-1164_P100-1999 2 ★ibm-1164_P100-1999 ibm-1164
ibm-1364_P110-2007 3 ★ibm-1364_P110-2007 ibm-1364 x-IBM1364
ibm-1371_P100-1999 3 ★ibm-1371_P100-1999 ibm-1371 x-IBM1371
ibm-1388_P103-2001 4 ★ibm-1388_P103-2001 ibm-1388 ibm-9580 x-IBM1388
ibm-1390_P110-2003 3 ★ibm-1390_P110-2003 ibm-1390 x-IBM1390
ibm-1399_P110-2003 3 ★ibm-1399_P110-2003 ibm-1399 x-IBM1399
ibm-5123_P100-1999 2 ★ibm-5123_P100-1999 ibm-5123
ibm-8482_P100-1999 2 ★ibm-8482_P100-1999 ibm-8482
ibm-16684_P110-2003 3 ★ibm-16684_P110-2003 ibm-16684 ibm-20780
ibm-4899_P100-1998 2 ★ibm-4899_P100-1998 ibm-4899
ibm-4971_P100-1999 2 ★ibm-4971_P100-1999 ibm-4971
ibm-9067_X100-2005 2 ★ibm-9067_X100-2005 ibm-9067
ibm-12712_P100-1998 3 ★ibm-12712_P100-1998 ibm-12712 ebcdic-he
ibm-16804_X110-1999 3 ★ibm-16804_X110-1999 ibm-16804 ebcdic-ar
ibm-37_P100-1995,swaplfnl 2 ★ibm-37_P100-1995,swaplfnl ibm-37-s390
ibm-1047_P100-1995,swaplfnl 3 ★ibm-1047_P100-1995,swaplfnl ibm-1047-s390 IBM1047_LF
ibm-1140_P100-1997,swaplfnl 2 ★ibm-1140_P100-1997,swaplfnl ibm-1140-s390
ibm-1141_P100-1997,swaplfnl 3 ★ibm-1141_P100-1997,swaplfnl ibm-1141-s390 IBM1141_LF
ibm-1142_P100-1997,swaplfnl 2 ★ibm-1142_P100-1997,swaplfnl ibm-1142-s390
ibm-1143_P100-1997,swaplfnl 2 ★ibm-1143_P100-1997,swaplfnl ibm-1143-s390
ibm-1144_P100-1997,swaplfnl 2 ★ibm-1144_P100-1997,swaplfnl ibm-1144-s390
ibm-1145_P100-1997,swaplfnl 2 ★ibm-1145_P100-1997,swaplfnl ibm-1145-s390
ibm-1146_P100-1997,swaplfnl 2 ★ibm-1146_P100-1997,swaplfnl ibm-1146-s390
ibm-1147_P100-1997,swaplfnl 2 ★ibm-1147_P100-1997,swaplfnl ibm-1147-s390
ibm-1148_P100-1997,swaplfnl 2 ★ibm-1148_P100-1997,swaplfnl ibm-1148-s390
ibm-1149_P100-1997,swaplfnl 2 ★ibm-1149_P100-1997,swaplfnl ibm-1149-s390
ibm-1153_P100-1999,swaplfnl 2 ★ibm-1153_P100-1999,swaplfnl ibm-1153-s390
ibm-12712_P100-1998,swaplfnl 2 ★ibm-12712_P100-1998,swaplfnl ibm-12712-s390
ibm-16804_X110-1999,swaplfnl 2 ★ibm-16804_X110-1999,swaplfnl ibm-16804-s390
ebcdic-xml-us 1 ★ebcdic-xml-us


ICU4Cを使ったプログラムをリリースするときは必要なDLLも一緒にexeファイルと同じディレクトリに置いて配布する必要があります。Win32プロジェクトなら[ICU配置パス]\icu\bin\icuuc**.dllが必要になります。x64プロジェクトなら[ICU配置パス]\icu\bin64\icuuc**.dllが必要になります。プロジェクト設定によりbin or bin64\追加libファイル名+バージョン番号.dllという具合にライブラリと対になってると思っていいです。できれば配布する前に開発環境が導入されていない通常のPCで動作チェックするのがよいです。環境が準備できないなら誰かに人柱になってもらって、確認がとれてから正式リリースとか、そういう風にした方がよいです。自分は以前に配布するべきファイルセットを間違えて、迷惑をかけたことがありますので、チェックするようにしています。

文字列大文字小文字変換

これは、まぁ関数がありますので簡単に実現できます。 aBcdEfgに書かれているものをABCDEFGに変換したり、あるいはabcdifgに変換したりできます。AbCDeFGみたいに入れ替えたり、英語の自然表記のため文章の始めの文字を探したり、略語や固有名詞の先頭文字を大文字にし、そのほかを小文字にするのはまた別の話になります。入れ替えくらいなら機械的な操作なのでやれると思います。大文字と小文字を同一視する必要のある検索の一致や大文字と小文字を区別しないメールアドレスやドメイン名の文字列変換に利用することもあります。この変換にも先述したICUを利用することができますが、この程度であれば標準関数を利用した方がよいです。DLLファイルを配ったり、プロジェクトにLibライブラリを読み込んだりINCLUDEファイルを設定したり、大きな実行ファイル群を要することになります。どうせ配布するならICUを使った方がよかったりする場合もありますので、ケースバイケースでしょうか?


とりあえずは、ICUの方の変換を記載しておきます。大文字、小文字変換の他に日本語ひらがなとカタカナとローマ字の相互変換についてもサンプルを示しました。ICUを使えば、簡単に変換してくれます。完璧ではなさそうなので、一括変換する場合は注意して使う必要はありそうです。

#pragma once

#include "stdafx.h"
#include <stdlib.h>
#include <string>		//std::string型定義
#include <mbstring.h>	//mbs***関数
#include <iostream>		//cpp cout etc 一般入出力関数
#include <locale>		//Locale関数
#include <tchar.h>		//TCHAR型+_tcs***関数
#include "atlstr.h"		//CString,CStringA,CStringWおよびLPTSTR,LPWSTR,LPSTR,LPCTSTR,LPCWSTR,LPCSTRの定義プリプロセッサ
#include "atlbase.h"	//同上
#include "cstringt.h"	//CStringTの定義プリプロセッサ

//_bstr_t型を利用するプリプロセッサ
#include "comutil.h"
#ifdef _DEBUG
#pragma comment(lib, "comsuppwd.lib")
#else
#pragma comment(lib, "comsuppw.lib")
#endif
//_bstr_t

//ICU ucnvプリプロセッサ
#include <unicode/ucnv.h>
#include <unicode/translit.h>
#ifdef _DEBUG
//#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuind.lib")//ICU Transliterate関数を使うために必要なライブラリ 正規表現関数も
#pragma comment(lib, "icuucd.lib")//ICU ucnvを使うために必要なライブラリ
//#pragma comment(lib, "icuiod.lib")
#else
//#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuind.lib")
#pragma comment(lib, "icuuc.lib")
//#pragma comment(lib, "icuio.lib")
#endif


using namespace std;

{
  _tsetlocale(LC_ALL, _T("Japanese"));


  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  // 全角 → 半角変換
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  UErrorCode errorNum = U_ZERO_ERROR;
  int nStrSize;
  
  //変換元となる全角リテラル
  std::wstring wstringResult = L"あいうえおアイウエオABCDE@;:*+/";
  UnicodeString str(wstringResult.c_str());//変換関数にはUnicodeString型を使うので初期値関数を使って初期化。strは変更可能な変数である必要がある。ココでconstは駄目。

  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  // 半角 → 全角変換
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  str = L"あいうえおアイウエオABCDE@;:*+/";
  Transliterator *myTrans = myTrans->createInstance(L"Halfwidth-Fullwidth", UTRANS_FORWARD, errorNum);
  myTrans->transliterate(str);
  printf("★半角 → 全角変換\n");
  wprintf(L"%s:\n",str.getTerminatedBuffer());
  printf("\n");
  
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  // 小文字 → 大文字変換
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  str = L"AbcDefGhiJKlmloPQRsTUvWxYzabcDeFgHiJkLmNoPQRstuvwxyZ";
  //delete[] myTrans;
  myTrans = myTrans->createInstance(L"lt-Upper", UTRANS_FORWARD, errorNum);
  myTrans->transliterate(str);
  printf("★小文字 → 大文字変換\n");
  wprintf(L"%s:\n",str.getTerminatedBuffer());
  printf("\n");


  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  // 大文字 → 小文字変換
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  str = L"AbcDefGhiJKlmloPQRsTUvWxYzabcDeFgHiJkLmNoPQRstuvwxyZ";
  //delete[] myTrans;
  myTrans = myTrans->createInstance(L"lt-Lower", UTRANS_FORWARD, errorNum);
  myTrans->transliterate(str);
  printf("★大文字 → 小文字変換\n");
  wprintf(L"%s:\n",str.getTerminatedBuffer());
  printf("\n");

  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  // カタカナ → ひらがな変換
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  str = L"あいうえおアイウエオギャギュギョアイウエオABCDE@;:*+/";
  //delete[] myTrans;
  printf("★カタカナ → ひらがな\n");
  wprintf(L"%s:\n",str.getTerminatedBuffer());
  myTrans = myTrans->createInstance(L"Katakana-Hiragana", UTRANS_FORWARD, errorNum);
  myTrans->transliterate(str);
  wprintf(L"%s:\n",str.getTerminatedBuffer());
  printf("\n");

  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  // ひらがな → カタカナ変換
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  str = L"あいうえおぎゃぎゅぎょアイウエオギャギュギョアイウエオABCDE@;:*+/";
  //delete[] myTrans;
  printf("★ひらがな → カタカナ\n");
  wprintf(L"%s:\n",str.getTerminatedBuffer());
  myTrans = myTrans->createInstance(L"Hiragana-Katakana", UTRANS_FORWARD, errorNum);
  myTrans->transliterate(str);
  wprintf(L"%s:\n",str.getTerminatedBuffer());
  printf("\n");

  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  // ひらがな → カタカナ→半角変換
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  str = L"あいうえおぎゃぎゅぎょアイウエオギャギュギョアイウエオABCDE@;:*+/";
  //delete[] myTrans;
  printf("★ひらがな → カタカナ→半角変換\n");
  wprintf(L"%s:\n",str.getTerminatedBuffer());
  myTrans = myTrans->createInstance(L"Hiragana-Katakana", UTRANS_FORWARD, errorNum);
  myTrans->transliterate(str);
  myTrans = myTrans->createInstance(L"Fullwidth-Halfwidth", UTRANS_FORWARD, errorNum);
  myTrans->transliterate(str);
  wprintf(L"%s:\n",str.getTerminatedBuffer());
  printf("\n");

  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  // ひらがな → ローマ字変換
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  str = L"あいうえおぎゃぎゅぎょアイウエオギャギュギョアイウエオABCDE@;:*+/";
  //delete[] myTrans;
  printf("★ひらがな → ローマ字変換\n");
  wprintf(L"%s:\n",str.getTerminatedBuffer());
  myTrans = myTrans->createInstance(L"Hiragana-Latin", UTRANS_FORWARD, errorNum);
  myTrans->transliterate(str);
  wprintf(L"%s:\n",str.getTerminatedBuffer());
  printf("\n");


  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  // カタカナ → ローマ字変換
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  str = L"あいうえおアイウエオギャギュギョアイウエオABCDE@;:*+/";
  //delete[] myTrans;
  printf("★カタカナ → ローマ字変換\n");
  wprintf(L"%s:\n",str.getTerminatedBuffer());
  myTrans = myTrans->createInstance(L"Katakana-Latin", UTRANS_FORWARD, errorNum);
  myTrans->transliterate(str);
  wprintf(L"%s:\n",str.getTerminatedBuffer());
  printf("\n");

  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  // ローマ字 → ひらがな変換
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  str = L"aiueogyagyugyochachuchepyapyupyovuzazishikemeアイウエオギャギュギョアイウエオABCDE@;:*+/";
  //delete[] myTrans;
  printf("★ローマ字 → ひらがな変換\n");
  wprintf(L"%s:\n",str.getTerminatedBuffer());
  myTrans = myTrans->createInstance(L"Latin-Hiragana", UTRANS_FORWARD, errorNum);
  myTrans->transliterate(str);
  wprintf(L"%s:\n",str.getTerminatedBuffer());
  printf("\n");

  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  // ローマ字 → カタカナ変換
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  str = L"aiueogyagyugyochachuchepyapyupyovuzazishikemeアイウエオギャギュギョアイウエオABCDE@;:*+/";
  //delete[] myTrans;
  printf("★ローマ字 → カタカナ変換\n");
  wprintf(L"%s:\n",str.getTerminatedBuffer());
  myTrans = myTrans->createInstance(L"Latin-Katakana", UTRANS_FORWARD, errorNum);
  myTrans->transliterate(str);
  wprintf(L"%s:\n",str.getTerminatedBuffer());
  printf("\n");

  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  // ローマ字 → カタカナ→半角変換
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  str = L"aiueogyagyugyochachuchepyapyupyovuzazishikemeアイウエオギャギュギョアイウエオABCDE@;:*+/";
  //delete[] myTrans;
  printf("★ローマ字 → カタカナ→半角変換\n");
  wprintf(L"%s:\n",str.getTerminatedBuffer());
  myTrans = myTrans->createInstance(L"Latin-Katakana", UTRANS_FORWARD, errorNum);
  myTrans->transliterate(str);
  myTrans = myTrans->createInstance(L"Fullwidth-Halfwidth", UTRANS_FORWARD, errorNum);
  myTrans->transliterate(str);
  wprintf(L"%s:\n",str.getTerminatedBuffer());
  printf("\n");

}

出力結果

★小文字 → 大文字変換
ABCDEFGHIJKLMLOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ:

★大文字 → 小文字変換
abcdefghijklmlopqrstuvwxyzabcdefghijklmnopqrstuvwxyz:

★カタカナ → ひらがな
あいうえおアイウエオギャギュギョアイウエオABCDE@;:*+/:
あいうえおあいうえおぎゃぎゅぎょあいうえおABCDE@;:*+/:

★ひらがな → カタカナ
あいうえおぎゃぎゅぎょアイウエオギャギュギョアイウエオABCDE@;:*+/:
アイウエオギャギュギョアイウエオギャギュギョアイウエオABCDE@;:*+/:

★ひらがな → カタカナ→半角変換
あいうえおぎゃぎゅぎょアイウエオギャギュギョアイウエオABCDE@;:*+/:
アイウエオギャギュギョアイウエオギャギュギョアイウエオABCDE@;:*+/:

★ひらがな → ローマ字変換
あいうえおぎゃぎゅぎょアイウエオギャギュギョアイウエオABCDE@;:*+/:
aiueogyagyugyoアイウエオg~ャg~ュg~ョアイウエオABCDE@;:*+/:

★カタカナ → ローマ字変換
あいうえおアイウエオギャギュギョアイウエオABCDE@;:*+/:
あいうえおaiueogyagyugyoaiueoABCDE@;:*+/:

★ひらがな → ひらがな変換
aiueogyagyugyochachuchepyapyupyovuzazishikemeアイウエオギャギュギョアイウエオABCDE@;:*+/:
あいうえおぎゃぎゅぎょちゃちゅちぇぴゃぴゅぴょヴざぜぃしけめアイウエオギャギュギョアイウエオあぶくで@;:*+/:

★ひらがな → カタカナ変換
aiueogyagyugyochachuchepyapyupyovuzazishikemeアイウエオギャギュギョアイウエオABCDE@;:*+/:
アイウエオギャギュギョチャチュチェピャピュピョヴザゼィシケメアイウエオギャギュギョアイウエオアブクデ@;:*+/:

★ひらがな → カタカナ→半角変換
aiueogyagyugyochachuchepyapyupyovuzazishikemeアイウエオギャギュギョアイウエオABCDE@;:*+/:
アイウエオギャギュギョチャチュチェピャピュピョヴザゼィシケメアイウエオギャギュギョアイウエオアブクデ@;:*+/:

ひらがな→ローマ字変換が残念な結果になっているようでして、ひらがなと一緒に記述していたカタカナのギャギュギョをg~ャg~ュg~ョと変換してしまったようです。こんな変換をプログラムで提供することは稀れだと思いますが、ICUのバグのようですので、注意して使いましょう。ひらがなローマ字変換とか変換遊びの範囲まで来てるような気がします。やってみようとするところは凄いと思います。ほかにもたくさんの変換マップがあるようです。テキストエディタで特殊な変換を提供してみるのもおもしろいのかもしれません。使いこなせるユーザの方が少なさそうです。半角全角変換も同じような要領で実施できます。これらの変換は基本的にはUnicodeの中で実施され、その後、Unicodeから違う文字コードへの変換をするといった作業にで文字コードへの対応を実現します。次の項目では、これらの変換と同時に文字コードの変換を行う手法についても触れています。


と、その前に、ICUを使わない大文字小文字変換もあるのでそのサンプルを記述します。利用する関数は


大文字→小文字変換

_strlwr、_wcslwr、_mbslwr、_strlwr_l、_wcslwr_l、_mbslwr_l

http://msdn.microsoft.com/ja-jp/library/vstudio/hkxwh33z.aspx

_strlwr_s、_strlwr_s_l、_mbslwr_s、_mbslwr_s_l、_wcslwr_s、_wcslwr_s_l

http://msdn.microsoft.com/ja-jp/library/vstudio/y889wzfw.aspx

***lwr***系の関数になります。例によって_str***に_wcs***、_mbs***と接尾句に****_sや****_lそして****_s_lがつくものの組み合わせが存在しています。


小文字→大文字変換

_strupr、_strupr_l、_mbsupr、_mbsupr_l、_wcsupr_l、_wcsupr

http://msdn.microsoft.com/ja-jp/library/vstudio/sch3dy08.aspx

_strupr_s、_strupr_s_l、_mbsupr_s、_mbsupr_s_l、_wcsupr_s、_wcsupr_s_l

http://msdn.microsoft.com/ja-jp/library/vstudio/sae941fh.aspx

***upr***系の関数になります。こちらも_str***に_wcs***、_mbs***と接尾句に****_sや****_lそして****_s_lがつくものの組み合わせが存在しています。

#pragma once
#include "stdafx.h"
#include <stdlib.h>
#include <string>		//std::string型定義
#include <mbstring.h>	//mbs***関数
#include <iostream>		//cpp cout etc 一般入出力関数
#include <locale>		//Locale関数
#include <tchar.h>		//TCHAR型+_tcs***関数
#include "atlstr.h"		//CString,CStringA,CStringWおよびLPTSTR,LPWSTR,LPSTR,LPCTSTR,LPCWSTR,LPCSTRの定義プリプロセッサ
#include "atlbase.h"	//同上
#include "cstringt.h"	//CStringTの定義プリプロセッサ
#include <vector>


//_bstr_t型を利用するプリプロセッサ
#include "comutil.h"
#ifdef _DEBUG
#pragma comment(lib, "comsuppwd.lib")
#else
#pragma comment(lib, "comsuppw.lib")
#endif
//_bstr_t

//ICU ucnvプリプロセッサ
#include <unicode/ucnv.h>
#include <unicode/translit.h>
#include <unicode/regex.h>
#ifdef _DEBUG
//#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuind.lib")//ICU Transliterate関数を使うために必要なライブラリ 正規表現関数も
#pragma comment(lib, "icuucd.lib")//ICU ucnvを使うために必要なライブラリ
//#pragma comment(lib, "icuiod.lib")
#else
#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuind.lib")
#pragma comment(lib, "icuuc.lib")
#pragma comment(lib, "icuio.lib")
#endif


#include "UnicodeConverter.h"


using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
	int nSizeValue;

	_wsetlocale( LC_ALL, L"Japanese");
	const char *ppcStr[]={"abCdefG","HijKLmn","OpQRSTu","vWxyZ"};

	nSizeValue = sizeof(ppcStr)/sizeof(*ppcStr);

	char** ppcStrConv = new char*[nSizeValue];

    printf("★大文字→小文字変換\n");
	for(int i = 0; i < nSizeValue; i++){
		*(ppcStrConv + i) = new char[strlen(*(ppcStr + i)) + 1];
		strcpy_s(*(ppcStrConv + i),strlen(*(ppcStr + i)) + 1, (*(ppcStr + i)));
	    _strlwr_s(*(ppcStrConv + i), strlen(*(ppcStrConv + i)) + 1);
	}

	for(int i = 0; i < nSizeValue; i++){
	    printf("%s\n",*(ppcStrConv + i));
	}

	for(int i = 0; i < nSizeValue; i++){
	    delete[] *(ppcStrConv + i);
	}


    printf("★小文字→大文字変換\n");
	for(int i = 0; i < nSizeValue; i++){
		*(ppcStrConv + i) = new char[strlen(*(ppcStr + i)) + 1];
		strcpy_s(*(ppcStrConv + i),strlen(*(ppcStr + i)) + 1, (*(ppcStr + i)));
	    _strupr_s(*(ppcStrConv + i), strlen(*(ppcStrConv + i)) + 1);
	}

	for(int i = 0; i < nSizeValue; i++){
	    printf("%s\n",*(ppcStrConv + i));
	}

	for(int i = 0; i < nSizeValue; i++){
	    delete[] *(ppcStrConv + i);
	}
  return 0;
}

出力結果

★大文字→小文字変換
abcdefg
hijklmn
opqrstu
vwxyz
★小文字→大文字変換
ABCDEFG
HIJKLMN
OPQRSTU
VWXYZ

上記のように変換できます。余談になりますが、配列はppcStr[i]のように記述できますが、結局のところ *(ppcStr + i)のように書き直すことができます。配列を宣言したときに、その変数は既にポインタ変数になっているので、宣言したときから、配列変数であることを示すプレフィックス(接頭句)としてpをつけておくのもよいですね。ポインタの配列はppと接頭句が付けられますし、char型ならppcという具合の接頭句の規則を自分は使っています。intはiじゃなくてnを接頭句に使うんですけど。面倒になって接頭句を付け忘れたりもします。そうするとしばらくして、これ何の変数だっけ?という具合になるおじいちゃんぶりを発揮します。たぶん、こうなってくると、「さっき食べたでしょ?おじいちゃん(^_^)。」って言われると、もう断食状態になります。やばいです。人が書いたプログラムと合わせると、また接頭句の規則が乱れて、自分自身では理解ができなくなってくるのです。たぶん自分は大きなプログラムを作るのには向いていませんね。せめて個人的に作っているプログラムの範囲では、規則どおりにやって間違いを減らしたいと思います。チームでやるときは、今までにつちかってきた規則と基盤を胸にきざみつつも応用力が試されます。int nSize[] = {10,20,30};みたいに宣言したときも実はnSizeってのは、この配列の先頭アドレスを保有する変数になってるわけだから、int pnSize[] = {10,20,30};として宣言して、*(pnSize + i) みたいにして、使うのもいいし、pnSize[i]として配列のように使うのもいい。ポインタ変数をあつかってるんだなぁって意識できてるだけマシ。プロになると、まぁそんな細かい事いわなくても自然と意識できるんでしょう。ただ、構造体やらクラスの配列だとpSize[i].mValueとかpSize[i].mFunc()みたいにできるものでも *(pSize + i).xxxとはできず、(*(pSize + i)).mValue のように全体をカッコでくくらないと駄目です。そして、アロー演算子は(*(ポインタ変数)).mValueのようなメンバ変数や関数の参照をおきかえるものでしたから、配列を[]を使わないで表現できることをしっていれば、(pSize + i)->mValueと記述を切り替えるのもたやすいですね。

***upr***や***lwr***関数のように与えられたポインタの実体を置き換える関数では、引数にconstやリテラルを与えることはできないのが常です。確保するメモリ領域を十分に考慮した実体化されたポインタ変数にコピーを作成して、それを引数にしなければなりません。関数によっては、実体の大きさを再定義して別のメモリ空間にさしかえてアドレスをポインタに格納して値を返却してくれます。こういった操作が多いので、動的にメモリを確保したりする手法を知っておくのは大事な知識となります。リテラルを使わないで変数宣言して十分な要素を確保してから、その変数のアドレスをポインタ変数に入れるという手法もありますが、文字数がどうなるかわからないプログラムでは、動的に確保するやり方をマスタしておかないと無駄にメモリを確保しなければならない場面が多くなってしまいます。配列や文字列を活用するには、動的なメモリ確保のやり方を把握しておいた方がいいですね。

文字列半角文字全角文字変換

これは、標準関数では対応できませんが、ABCD…UVWXYZ@…abc…xyzをABCD…UVWXYZ@…abc…xyzに変換するような作業です。文字コードによっても求める結果や操作が違うので、ICUを使うのが手っ取り早いでしょう。自分で作成できる範囲の変換処理でもあります。


以下はICUを利用した全角→半角変換です。

#pragma once

#include "stdafx.h"
#include <stdlib.h>
#include <string>		//std::string型定義
#include <mbstring.h>	//mbs***関数
#include <iostream>		//cpp cout etc 一般入出力関数
#include <locale>		//Locale関数
#include <tchar.h>		//TCHAR型+_tcs***関数
#include "atlstr.h"		//CString,CStringA,CStringWおよびLPTSTR,LPWSTR,LPSTR,LPCTSTR,LPCWSTR,LPCSTRの定義プリプロセッサ
#include "atlbase.h"	//同上
#include "cstringt.h"	//CStringTの定義プリプロセッサ

//_bstr_t型を利用するプリプロセッサ
#include "comutil.h"
#ifdef _DEBUG
#pragma comment(lib, "comsuppwd.lib")
#else
#pragma comment(lib, "comsuppw.lib")
#endif
//_bstr_t

//ICU ucnvプリプロセッサ
#include <unicode/ucnv.h>
#include <unicode/translit.h>
#ifdef _DEBUG
//#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuind.lib")//ICU Transliterate関数を使うために必要なライブラリ 正規表現関数も
#pragma comment(lib, "icuucd.lib")//ICU ucnvを使うために必要なライブラリ
//#pragma comment(lib, "icuiod.lib")
#else
#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuind.lib")
#pragma comment(lib, "icuuc.lib")
#pragma comment(lib, "icuio.lib")
#endif


using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
  _tsetlocale(LC_ALL, _T("Japanese"));


  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  // 全角 → 半角変換
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  UConverter* ucnv;
  UErrorCode errorNum = U_ZERO_ERROR;
  int nStrSize;
  
  //変換元となる全角リテラル
  std::wstring wstringResult = L"あいうえおアイウエオABCDE@;:*+/";
  UnicodeString str(wstringResult.c_str());//変換関数にはUnicodeString型を使うので初期値関数を使って初期化。strは変更可能な変数である必要がある。ココでconstは駄目。

  //正しい変換結果確認用のリテラル
  std::wstring wstringResult2 = L"あいうえおアイウエオABCDE@;:*+/";

  //変換処理の核となる部分
  //変換タイプリテラル
  const UnicodeString convert_type(L"Fullwidth-Halfwidth");
  Transliterator *myTrans = myTrans->createInstance(convert_type, UTRANS_FORWARD, errorNum);//変換方法を設定
  myTrans->transliterate(str);//変換 

  ucnv = ucnv_open("shift_jis", &errorNum);
  std::string stringResult(str.length() * ucnv_getMaxCharSize(ucnv),'\0');//shift_jisの最大文字サイズと文字数で仮のメモリ確保
  ucnv_close(ucnv);

  //UnicodeStringクラスを使った変換例。Unicode→任意の文字コード。
  //但しchar型へしか格納できない変数なのでUnicode→Unicodeのような無意味な変換には適さない。やれるけど…
  nStrSize = str.extract(0, str.length(), &stringResult[0], "shift_jis");//stringResultにShift_JISに変換した内容を保管。
  stringResult.resize(nStrSize);//変換によって判明したメモリサイズに再設定


  //UnicodeStringのwchar_w型文字列を格納している先頭アドレスを取得できるメンバ関数。
  printf("★UnicodeString変数の出力サンプル\n");
  wprintf(L"%s:\n",str.getTerminatedBuffer());
  printf("\n");

  //★半角変換+shift_jis変換のバイトコード表示にためにchar型へ代入して出力
  printf("★半角変換+shift_jis変換\n");
  char* pcStr2 = new char[strlen(stringResult.c_str()) + 1];
  strcpy_s(pcStr2,strlen(stringResult.c_str()) + 1,stringResult.c_str());
  printf("%s %d:\n",stringResult.c_str(),strlen(stringResult.c_str()) + 1);
  for(int i = 0;i < (int)strlen(stringResult.c_str()) + 1;i++){
    printf("%02x:",0x000000FF & *(pcStr2 + i));
  }
  printf("\n");
  printf("\n");


  //★無変換のバイトコード表示にためにw_char型へ代入して出力
  printf("★全角無変換\n");
  wchar_t* pcStr = new wchar_t[wcslen(wstringResult.c_str()) + 1];
  wcscpy_s(pcStr,wcslen(wstringResult.c_str()) + 1,wstringResult.c_str());
  wprintf(L"%s %d:\n",wstringResult.c_str(),wcslen(wstringResult.c_str()) + 1);
  for(int i = 0;i < (int)wcslen(wstringResult.c_str()) + 1;i++){
    printf("%02x:",0x0000FFFF & *(pcStr + i));
  }
  printf("\n");
  printf("\n");


  //★変換後確認用の手動変換リテラルのバイトコード表示にためにw_char型へ代入して出力
  printf("★手動変換(確認用)\n");
  wchar_t* pcStr3 = new wchar_t[wcslen(wstringResult2.c_str()) + 1];
  wcscpy_s(pcStr3,wcslen(wstringResult2.c_str()) + 1,wstringResult2.c_str());
  wprintf(L"%s %d:\n",wstringResult2.c_str(),wcslen(wstringResult2.c_str()) + 1);
  for(int i = 0;i < (int)wcslen(wstringResult2.c_str()) + 1;i++){
    printf("%02x:",0x0000FFFF & *(pcStr3 + i));
  }
  printf("\n");
  printf("\n");

  //★変換処理をしたUnicodeのバイトコード表示にためにw_char型へ代入して出力
  printf("★半角変換Unicode版\n");
  wchar_t* pcStr4 = new wchar_t[wcslen(str.getTerminatedBuffer()) + 1];
  wcscpy_s(pcStr4,wcslen(str.getTerminatedBuffer()) + 1,str.getTerminatedBuffer());
  wprintf(L"%s %d:\n",str.getTerminatedBuffer(),wcslen(str.getTerminatedBuffer()) + 1);
  for(int i = 0;i < (int)wcslen(str.getTerminatedBuffer()) + 1;i++){
    printf("%02x:",0x0000FFFF & *(pcStr4 + i));
  }
  printf("\n");
  printf("\n");

  retuen 0;
}

出力結果

★UnicodeString変数の出力サンプル
あいうえおアイウエオABCDE@;:*+/:

★半角変換+shift_jis変換
あいうえおアイウエオABCDE@;:*+/ 27:
82:a0:82:a2:82:a4:82:a6:82:a8:b1:b2:b3:b4:b5:41:42:43:44:45:40:3b:3a:2a:2b:2f:00:

★全角無変換
あいうえおアイウエオABCDE@;:*+/ 22:
3042:3044:3046:3048:304a:30a2:30a4:30a6:30a8:30aa:ff21:ff22:ff23:ff24:ff25:ff20:ff1b:ff1a:ff0a:ff0b:ff0f:0000:

★手動変換(確認用)
あいうえおアイウエオABCDE@;:*+/ 22:
3042:3044:3046:3048:304a:ff71:ff72:ff73:ff74:ff75:0041:0042:0043:0044:0045:0040:003b:003a:002a:002b:002f:0000:

★半角変換Unicode版
あいうえおアイウエオABCDE@;:*+/ 22:
3042:3044:3046:3048:304a:ff71:ff72:ff73:ff74:ff75:0041:0042:0043:0044:0045:0040:003b:003a:002a:002b:002f:0000:

という感じに変換ができます。ICUの使い方さえわかれば、いろいろできますね。記事を書きながら、自分もようやくわかってきた感じ。ぉぃ。


サンプルプログラムのコメントを読んでいただければ、理解できると思います。逆の変換はconverttypeを書き換えるだけです。


createInstanceで指定できる変換タイプ名は以下のとおりです。

Arabic-Latin
Arabic-Latin/BGN
Armenian-Latin
Armenian-Latin/BGN
Azerbaijani-Latin/BGN
Belarusian-Latin/BGN
Bengali-Devanagari
Bengali-Gujarati
Bengali-Gurmukhi
Bengali-Kannada
Bengali-Latin
Bengali-Malayalam
Bengali-Oriya
Bengali-Tamil
Bengali-Telugu
Bopomofo-Latin
Bulgarian-Latin/BGN
Cyrillic-Latin
Devanagari-Bengali
Devanagari-Gujarati
Devanagari-Gurmukhi
Devanagari-Kannada
Devanagari-Latin
Devanagari-Malayalam
Devanagari-Oriya
Devanagari-Tamil
Devanagari-Telugu
Digit-Tone
Fullwidth-Halfwidth
Georgian-Latin
Georgian-Latin/BGN
Greek-Latin
Greek-Latin/BGN
Greek-Latin/UNGEGN
Gujarati-Bengali
Gujarati-Devanagari
Gujarati-Gurmukhi
Gujarati-Kannada
Gujarati-Latin
Gujarati-Malayalam
Gujarati-Oriya
Gujarati-Tamil
Gujarati-Telugu
Gurmukhi-Bengali
Gurmukhi-Devanagari
Gurmukhi-Gujarati
Gurmukhi-Kannada
Gurmukhi-Latin
Gurmukhi-Malayalam
Gurmukhi-Oriya
Gurmukhi-Tamil
Gurmukhi-Telugu
Halfwidth-Fullwidth
Han-Latin
Han-Latin/Names
Hangul-Latin
Hans-Hant
Hant-Hans
Hebrew-Latin
Hebrew-Latin/BGN
Hiragana-Katakana
Hiragana-Latin
IPA-XSampa
Jamo-Latin
Kannada-Bengali
Kannada-Devanagari
Kannada-Gujarati
Kannada-Gurmukhi
Kannada-Latin
Kannada-Malayalam
Kannada-Oriya
Kannada-Tamil
Kannada-Telugu
Katakana-Hiragana
Katakana-Latin
Katakana-Latin/BGN
Kazakh-Latin/BGN
Kirghiz-Latin/BGN
Korean-Latin/BGN
Latin-ASCII
Latin-Arabic
Latin-Armenian
Latin-Bengali
Latin-Bopomofo
Latin-Cyrillic
Latin-Devanagari
Latin-Georgian
Latin-Greek
Latin-Greek/UNGEGN
Latin-Gujarati
Latin-Gurmukhi
Latin-Hangul
Latin-Hebrew
Latin-Hiragana
Latin-Jamo
Latin-Kannada
Latin-Katakana
Latin-Malayalam
Latin-NumericPinyin
Latin-Oriya
Latin-Syriac
Latin-Tamil
Latin-Telugu
Latin-Thaana
Latin-Thai
Macedonian-Latin/BGN
Malayalam-Bengali
Malayalam-Devanagari
Malayalam-Gujarati
Malayalam-Gurmukhi
Malayalam-Kannada
Malayalam-Latin
Malayalam-Oriya
Malayalam-Tamil
Malayalam-Telugu
Maldivian-Latin/BGN
Mongolian-Latin/BGN
NumericPinyin-Latin
NumericPinyin-Pinyin
Oriya-Bengali
Oriya-Devanagari
Oriya-Gujarati
Oriya-Gurmukhi
Oriya-Kannada
Oriya-Latin
Oriya-Malayalam
Oriya-Tamil
Oriya-Telugu
Pashto-Latin/BGN
Persian-Latin/BGN
Pinyin-NumericPinyin
Publishing-Any
Russian-Latin/BGN
Serbian-Latin/BGN
Simplified-Traditional
Syriac-Latin
Tamil-Bengali
Tamil-Devanagari
Tamil-Gujarati
Tamil-Gurmukhi
Tamil-Kannada
Tamil-Latin
Tamil-Malayalam
Tamil-Oriya
Tamil-Telugu
Telugu-Bengali
Telugu-Devanagari
Telugu-Gujarati
Telugu-Gurmukhi
Telugu-Kannada
Telugu-Latin
Telugu-Malayalam
Telugu-Oriya
Telugu-Tamil
Thaana-Latin
Thai-Latin
Tone-Digit
Traditional-Simplified
Turkmen-Latin/BGN
Ukrainian-Latin/BGN
Uzbek-Latin/BGN
XSampa-IPA
az-Lower
az-Title
az-Upper
ch-ch_FONIPA
cs-cs_FONIPA
cs-ja
cs-ko
cs_FONIPA-ja
cs_FONIPA-ko
dsb-dsb_FONIPA
el-Lower
el-Title
el-Upper
es-am
es-es_FONIPA
es-ja
es-zh
es_419-ja
es_419-zh
es_FONIPA-am
es_FONIPA-es_419_FONIPA
es_FONIPA-ja
es_FONIPA-zh
it-am
it-ja
ja_Latn-ko
ja_Latn-ru
la-la_FONIPA
lt-Lower
lt-Title
lt-Upper
nl-Title
pl-ja
pl-pl_FONIPA
pl_FONIPA-ja
ro-ja
ro-ro_FONIPA
ro_FONIPA-ja
ru-ja
ru-zh
sk-ja
sk-sk_FONIPA
sk_FONIPA-ja
tlh-tlh_FONIPA
tr-Lower
tr-Title
tr-Upper
uz_Cyrl-uz_Latn
uz_Latn-uz_Cyrl
yo-yo_BJ
zh_Latn_PINYIN-ru
Any-Null
Any-Lower
Any-Upper
Any-Title
Any-Name
Name-Any
Any-Remove
Any-Hex/Unicode
Any-Hex/Java
Any-Hex/C
Any-Hex/XML
Any-Hex/XML10
Any-Hex/Perl
Any-Hex
Hex-Any/Unicode
Hex-Any/Java
Hex-Any/C
Hex-Any/XML
Hex-Any/XML10
Hex-Any/Perl
Hex-Any
Any-NFC
Any-NFKC
Any-NFD
Any-NFKD
Any-FCD
Any-FCC
Any-ch_FONIPA
Any-Latin
Any-Telugu
Any-Gurmukhi
Any-Gujarati
Any-Malayalam
Any-Oriya
Any-Devanagari
Any-Kannada
Any-Tamil
Any-cs_FONIPA
Any-ru
Any-Bengali
Any-uz_Latn
Any-Katakana
Any-ro_FONIPA
Any-zh
Any-yo_BJ
Any-am
Any-es_419_FONIPA
Any-es_FONIPA
Any-sk_FONIPA
Any-Hant
Any-Hans
Any-Hiragana
Any-la_FONIPA
Any-dsb_FONIPA
Any-Syriac
Any-Greek
Any-Greek/UNGEGN
Any-Cyrillic
Any-Hangul
Any-Bopomofo
Any-Arabic
Any-Thai
Any-Armenian
Any-Thaana
Any-Georgian
Any-Hebrew
Any-uz_Cyrl

文字列の検索と置換・正規表現

文字列の検索と置換は、メモ帳でCtrl+Fとかで検索するようなものや置換する処理と同じように目的の文字列を探す方法とファイルの検索のようにワイルドカードを使う方法があり、検索して一致した文字列を置き換えるという処理も同じみですが、もうひとつの検索と置換のやり方として、正規表現(Regular Expresion)という手法があります。これはワイルドカードによる指定にさらに文字列の先頭にある特定の文字列があって、さらに特定の文字列にはさまれた部分があって、文字列の最後尾にも特定の文字があったら、その挟まれた文字の先頭グループ1、グループ2、…グループnとして、順次、変換法則にしたがって変換といった複雑な指定が可能になる手法です。正規表現は複雑なこともできるし、単純なこともできる。できるだけ複雑なモノを単純にしてから操作をする手法など、検索と置換には多岐にわたる手法が存在します。また、検索効率をあげる手法もありかなり文字列操作の中でも奥深い技術を必要とする操作です。まずは単純な検索と置換。それと簡単な正規表現にチャレンジしてみましょう。正規表現の使い方をここでは取り上げて、正規表現による問題解決手法は、ここ以外での説明にしたいと思います。

以下、ICUのRegexを使った検索と置換の正規表現による処理のサンプルです。

#pragma once

#include "stdafx.h"
#include <stdlib.h>
#include <string>		//std::string型定義
#include <mbstring.h>	//mbs***関数
#include <iostream>		//cpp cout etc 一般入出力関数
#include <locale>		//Locale関数
#include <tchar.h>		//TCHAR型+_tcs***関数
#include "atlstr.h"		//CString,CStringA,CStringWおよびLPTSTR,LPWSTR,LPSTR,LPCTSTR,LPCWSTR,LPCSTRの定義プリプロセッサ
#include "atlbase.h"	//同上
#include "cstringt.h"	//CStringTの定義プリプロセッサ
#include <vector>


//_bstr_t型を利用するプリプロセッサ
#include "comutil.h"
#ifdef _DEBUG
#pragma comment(lib, "comsuppwd.lib")
#else
#pragma comment(lib, "comsuppw.lib")
#endif
//_bstr_t

//ICU ucnvプリプロセッサ
#include <unicode/ucnv.h>
#include <unicode/translit.h>
#include <unicode/regex.h>
#ifdef _DEBUG
//#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuind.lib")//ICU Transliterate関数を使うために必要なライブラリ 正規表現関数も
#pragma comment(lib, "icuucd.lib")//ICU ucnvを使うために必要なライブラリ
//#pragma comment(lib, "icuiod.lib")
#else
#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuind.lib")
#pragma comment(lib, "icuuc.lib")
#pragma comment(lib, "icuio.lib")
#endif


#include "UnicodeConverter.h"


using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
  
  _tsetlocale(LC_ALL, _T("Japanese"));


  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  // 正規表現分割
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  UParseError error;
  UErrorCode  status = U_ZERO_ERROR;
  int nFindCnt = 0;
 

  printf("★正規表現検索\n");
  UnicodeString regex = L"[,/とやも]";//正規表現検索文字
  RegexPattern* pattern = RegexPattern::compile(regex, error, status);//パターンを登録(コンパイル)
  assert( U_SUCCESS(status) );
 
  UnicodeString input = L"リンゴとミカン/ナシとモモ,ごはんやおかず/おさけもつまみ,programと技術";
  assert( U_SUCCESS(status) );
  RegexMatcher* matcher = pattern->matcher(input, status);//入力値での正規表現検索処理をmatcherへ格納


  printf("★パターンマッチ結果出力\n");
  while ( matcher->find() ) {
   //それぞれのマッチグループの出力。パターンに部分一致(.)が無い場合はつねにグループは1で要素0番のみ 
   for ( int32_t i = 0; i <= matcher->groupCount(); i++ ) {
      std::wcout << L"[" << matcher->start(i,status)
                 << L"," << matcher->end(i,status)
                 << L"]" << matcher->group(i,status).getTerminatedBuffer()
                 << std::endl;
      assert( U_SUCCESS(status) );
    }
    //whileループ回数をカウントして分割数を記録
    nFindCnt++;
    std::wcout << std::endl;
  }
 
  //分割した回数のUnicodeString変数の配列を確保して分割結果を配列に格納。
  printf("★検索された分割文字トークンの数\n");
  std::wcout << nFindCnt << std::endl;
  printf("\n");

  UnicodeString* pfruits = new UnicodeString[nFindCnt + 1]; 
   int32_t splits = pattern->split(input, pfruits, nFindCnt + 1, status);//登録されたパターンで分割処理
  //splitの引数には入力文字列,要素が確保された出力文字列配列変数,分割数,実行結果
 
  assert( U_SUCCESS(status) );
  printf("★分割抽出した配列の出力\n");
  //分割によりそれぞれに格納された文字を文字列配列の全出力で確認
  for ( int32_t i = 0; i < splits; i++ ) {
    std::wcout << pfruits[i].getTerminatedBuffer() << std::endl;
    //std::wcout << (*(pfruits + i)).getTerminatedBuffer() << std::endl;こうやって表現しても同じ。
  }
  printf("\n");
 
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  // 正規表現置換
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  UnicodeString replacement = L"+";

 
  /*
   * 先頭置換 : replaceFirst
   */
  printf("★先頭置換\n");
  UnicodeString result = matcher->replaceFirst(replacement, status);
  assert( U_SUCCESS(status) );
  std::wcout << result.getTerminatedBuffer() << std::endl;
 
  /*
   * 一括置換 : replaceAll
   */
  printf("★一括置換\n");
  result = matcher->replaceAll(replacement, status);
  assert( U_SUCCESS(status) );
  std::wcout << result.getTerminatedBuffer() << std::endl;
 
  /*
   * 追記置換 : appendReplacement/appendAll 前方から逐次置換してresultに追加していく方式
   */
  printf("★追記置換\n");
  result = L"";
  matcher->reset();
  while ( matcher->find() ) {
    matcher->appendReplacement(result, replacement, status);
    assert( U_SUCCESS(status) );
    //最後の置換完了状態で一時出力
    std::wcout << result.getTerminatedBuffer() << std::endl;
  }
 
 
  //最後の置換以降の残った文字列を追加
  matcher->appendTail(result);
  std::wcout << result.getTerminatedBuffer() << std::endl;
 
  printf("\n");
 
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  // 部分一致
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  /*
   * 部分マッチ"(.)"の置換
  */
  regex = L"(.)([,/とやも]|$)";//正規表現検索文字 区切り文字or終端の一文字手前 $1を置換 区切り文字or終端は $2
  replacement = L"★";//伏字に置換
  result = input;

  pattern = RegexPattern::compile(regex, error, status);
  matcher = pattern->matcher(input, status);


  printf("★パターンマッチ結果出力\n");
  while ( matcher->find() ) {
   //それぞれのマッチグループの出力。パターンに部分一致(.)が無い場合はつねにグループは1で要素0番のみ 
   for ( int32_t i = 0; i <= matcher->groupCount(); i++ ) {
      std::wcout << L"[" << matcher->start(i,status)
                 << L"," << matcher->end(i,status)
                 << L"]" << matcher->group(i,status).getTerminatedBuffer()
                 << std::endl;
      assert( U_SUCCESS(status) );
    }
    //whileループ回数をカウントして分割数を記録
    nFindCnt++;
    std::wcout << std::endl;
  }


  typedef std::pair<int32_t,int32_t> range_type;
  std::vector<range_type> matches;


  matcher->reset(); 
  while ( matcher->find() ) {
    // 第一グループを保存する 条件:$1 が存在して、検索の開始文字位置と終了文字位置が0以上なら保存
	if(matcher->groupCount() >= 1 && matcher->start(1,status) > 0 && matcher->end(1,status) > 0 ){
      matches.push_back(range_type(matcher->start(1,status), matcher->end(1,status)));
      assert( U_SUCCESS(status) );
	}
  } 
  // 末尾から置換
  while ( !matches.empty() ) { 
    range_type range = matches.back();
    matches.pop_back();
    result.replace(range.first, range.second - range.first, replacement);
  }
  printf("★部分一致パターンの置換結果出力\n");
  std::wcout << result.getTerminatedBuffer() << std::endl;
  
  delete[] pfruits;
  delete matcher;
  delete pattern;


  return 0;
}

出力結果

★正規表現検索
★パターンマッチ結果出力
[3,4]と

[7,8]/

[10,11]と

[13,14],

[17,18]や

[21,22]/

[25,26]も

[29,30],

[37,38]と

★検索された分割文字トークンの数
9

★分割抽出した配列の出力
リンゴ
ミカン
ナシ
モモ
ごはん
おかず
おさけ
つまみ
program
技術

★先頭置換
リンゴ+ミカン/ナシとモモ,ごはんやおかず/おさけもつまみ,programと技術
★一括置換
リンゴ+ミカン+ナシ+モモ+ごはん+おかず+おさけ+つまみ+program+技術
★追記置換
リンゴ+
リンゴ+ミカン+
リンゴ+ミカン+ナシ+
リンゴ+ミカン+ナシ+モモ+
リンゴ+ミカン+ナシ+モモ+ごはん+
リンゴ+ミカン+ナシ+モモ+ごはん+おかず+
リンゴ+ミカン+ナシ+モモ+ごはん+おかず+おさけ+
リンゴ+ミカン+ナシ+モモ+ごはん+おかず+おさけ+つまみ+
リンゴ+ミカン+ナシ+モモ+ごはん+おかず+おさけ+つまみ+program+
リンゴ+ミカン+ナシ+モモ+ごはん+おかず+おさけ+つまみ+program+技術

★パターンマッチ結果出力
[2,4]ゴと
[2,3]ゴ
[3,4]と

[6,8]ン/
[6,7]ン
[7,8]/

[9,11]シと
[9,10]シ
[10,11]と

[12,14]モ,
[12,13]モ
[13,14],

[16,18]んや
[16,17]ん
[17,18]や

[20,22]ず/
[20,21]ず
[21,22]/

[24,26]けも
[24,25]け
[25,26]も

[28,30]み,
[28,29]み
[29,30],

[36,38]mと
[36,37]m
[37,38]と

[39,40]術
[39,40]術
[40,40]

★部分一致パターンの置換結果出力
リン★とミカ★/ナ★とモ★,ごは★やおか★/おさ★もつま★,progra★と技★

149行目以降に記述した部分一致からのプログラムはやや複雑な表記になっていますが、これはICUに準備された関数だけでは部分一致の処理ができないために自分で工夫をしなければならないことに起因しています。


169行目のtypedef関数は宣言を置き換えるための命令文になります。std::pair<int32_t,int32_t>という構造体定義をrange_typeに置き換えるということを意味しています。 またpairの後ろにある<int32_t,int32_t>というのはpairという構造体がテンプレート構造体として準備されていて、二つの型名を引数にとる必要がありここではint32_tという型を2つ指定して、pairというテンプレート構造体で使う2つの型はどちらもint型で使いますというテンプレート構造体の利用方法を記述している部分になります。pair構造体は2つの変数を対にして記憶するための構造体になっています。


170行目のvectorという関数もテンプレートクラスになっていて、要するにstd::vector< std::pair<int32_t,int32_t> >という宣言をしたのと同じことで、vectorクラスはstd::pair<int32_t,int32_t>という型で使いますという準備をした上でその変数として、matchesというものを使うという定義になります。vecotrは動的にメモリを確保するのを支援してくれるクラスで、push_backという関数の引数にstd::pair<int32_t,int32_t>(int型の数値,int型の数値)という構造体が持つコンストラクタによって返されるpair構造体型のポインタを記録しています。引数には検索した文字の開始位置と終了位置を入れています。vectorのbackという関数で最後に格納したポインタを返してくれるので、range_typeつまりstd::pair<int32_t,int32_t>型のrangeというポインタ変数にコピーしています。std::pairのメンバ変数firstにコンストラクタで使用した一つ目引数の数値が取得でき、メンバ変数secondで二つ目の引数の数値が取得できます。vecotorクラスのpop_back()という関数の呼び出しによって最後に格納したポインタを消すという処理をしてくれます。こうやってfind()で検索するすべての検索位置をvectorクラスの中にpair構造体でセットした2つのint型整数で検索された文字の開始位置と終了位置を記憶する仕組みです。テンプレートクラスやテンプレート構造体やクラスについて理解する必要があります。vectorのempty()関数は格納されたものが無くなったらtrueを返す関数で、while文のループ条件として!matches.empty()のようにして、空っぽではないあいだ繰り返すという処理になっています。


部分一致の処理は難しいので、typedef関数とクラスと構造体のコンストラクタとテンプレートクラスとクラスのメンバ関数およびクラスのメンバ変数、更には標準クラスであるvectorクラスとpair構造体について理解してから戻ってくるとよいかもしれません。


ICUを使わない文字列検索としてはstrchrのような***chr系の一文字検索関数があります。見つかった位置のByte数を返してくれます。ICUの正規表現に比べると機能性は低いです。一文字を引数にするとき符号なし整数型にしなければいけないあたりは、初期値を与えての検索は楽ですが、実際に一文字の文字列から検索しようとすると大変なのかもしれません。実際に数えてみないと、一文字あたりが、1バイトなのか2バイトなのかわからないし…。こういう一文字検索だけを行う関数って使う人いるんだろか?って思う。やっぱstrstr系使うよね。


以下のサンプルでは文字列の検索をするもので、以下の関数も使っています。 _strinc、_wcsinc、_mbsinc、_mbsinc_l のようなポインタ変数を1文字を進める関数。文字列の先頭を示していたものが次の文字になります。大元の文字列のポインタ変数を操作すると先頭文字を見失うので、通常は、同じアドレスを指し示すポインタ変数を複製して、複製したポインタ変数で操作します。 詳細は、以下のアドレスのとおりです。

http://msdn.microsoft.com/ja-jp/library/vstudio/ex0hs2ad.aspx

_strdec、_wcsdec、_mbsdec、_mbsdec_l 一文字戻るにはこのような関数があります。

http://msdn.microsoft.com/ja-jp/library/vstudio/cf10bexy.aspx


そして、strchr、wcschr、_mbschr、_mbschr_l は以下のような関数があって、ここでは_mbschrのサンプルを作りました。

http://msdn.microsoft.com/ja-jp/library/vstudio/b34ccac3.aspx


後方から検索するものとしては、strrchr、wcsrchr、_mbsrchr、_mbsrchr_lがあり、ここでは_mbsrchrを使っています。

http://msdn.microsoft.com/ja-jp/library/vstudio/ftw0heb9.aspx


更に文字列中を文字列で検索するものとしては、strstr、wcsstr、_mbsstr、_mbsstr_lがあり、ここでは_mbsstrを使っています。

http://msdn.microsoft.com/ja-jp/library/vstudio/z9da80kz.aspx


マルチバイト文字の1文字を数値として取得する関数として

_strnextc、_wcsnextc、_mbsnextc、_mbsnextc_lがあり、ここでは_mbsnetcを使いました。その下に関数を使わない場合の数値の取得方法についても処理を記述しました。1文字バイト構成を羅列したときの整数値に変換した数値を抜き出したいという特殊な用途に応える関数になっています。自分で計算するのは面倒な手続きが必要になりそうなことがサンプルからわかると思います。

http://msdn.microsoft.com/ja-jp/library/vstudio/5zsfy4ab.aspx

#pragma once
#include "stdafx.h"
#include <stdlib.h>
#include <string>		//std::string型定義
#include <mbstring.h>	//mbs***関数
#include <iostream>		//cpp cout etc 一般入出力関数
#include <locale>		//Locale関数
#include <tchar.h>		//TCHAR型+_tcs***関数
#include "atlstr.h"		//CString,CStringA,CStringWおよびLPTSTR,LPWSTR,LPSTR,LPCTSTR,LPCWSTR,LPCSTRの定義プリプロセッサ
#include "atlbase.h"	//同上
#include "cstringt.h"	//CStringTの定義プリプロセッサ
#include <vector>


//_bstr_t型を利用するプリプロセッサ
#include "comutil.h"
#ifdef _DEBUG
#pragma comment(lib, "comsuppwd.lib")
#else
#pragma comment(lib, "comsuppw.lib")
#endif
//_bstr_t

//ICU ucnvプリプロセッサ
#include <unicode/ucnv.h>
#include <unicode/translit.h>
#include <unicode/regex.h>
#ifdef _DEBUG
//#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuind.lib")//ICU Transliterate関数を使うために必要なライブラリ 正規表現関数も
#pragma comment(lib, "icuucd.lib")//ICU ucnvを使うために必要なライブラリ
//#pragma comment(lib, "icuiod.lib")
#else
#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuind.lib")
#pragma comment(lib, "icuuc.lib")
#pragma comment(lib, "icuio.lib")
#endif


#include "UnicodeConverter.h"


using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{

  int SingleChr = 'r';
  unsigned int mbSingleString;  
  unsigned int mbSingleStringNextC;
  unsigned int nKeta;

  char pcSingleStr[] = "列";
  char pcStr[] = "文字列";

  char pcString[] = "日本語文字の検索用文字列です。The quick brown dog jumps over the lazy fox";
  char fmt1[] =   "         1         2         3         4         5         6";
  char fmt2[] =   "123456789012345678901234567890123456789012345678901234567890123456789";

  char *pcPos;
  unsigned char *pucPos;
  unsigned char *pucRusultPos;

  int result;
  int result2;

  printf_s( "文字列検索:\n\n      %s\n", pcString );
  printf_s( "      %s\n      %s\n\n", fmt1, fmt2 );



  //★マルチバイト対応1文字の文字列で検索。
  printf_s( "★マルチバイト対応検索\n");

  pucPos = (unsigned char*)pcSingleStr;

  mbSingleStringNextC = _mbsnextc(pucPos);
  printf_s( "MultiByteString検索文字 SigngleWord:%04x(%s)\n", mbSingleStringNextC, pcSingleStr);
  
  pucPos = _mbsinc(pucPos);//一文字分ポインタを進める関数
  result = (int)(pucPos - (unsigned char*)pcSingleStr);
  mbSingleString = 0;
  nKeta = 1;
  for(int n = 0; n < result; n++){
    mbSingleString = mbSingleString + (*(pucPos - (n + 1))) * (nKeta);
    printf_s( "%02x \n", (*(pucPos - (n + 1))));
	nKeta = nKeta * 0x100;
  }

  //printf_s( "%02x%02x\n", *(pucPos - 2), *(pucPos - 1));このカタチをunsigned int の変数に変形したのが mbSingleString
  //unsigned int mbstringString = 'の';とかでも検索できますが、strcpy関数とかでコピーしてきた一文字の文字列でサンプル
  
  printf_s( "MultiByteString検索文字 SigngleWord:%04x(%s)\n", mbSingleString, pcSingleStr);
  printf_s( "incriment:%d[Byte]\n\n",result);

  pucRusultPos = _mbschr((unsigned char*)pcString, mbSingleString);
  result2 = (int)(pucRusultPos - (unsigned char*)pcString + 1);
  if ( pucRusultPos != NULL )
    printf_s( "検索結果:   最初の %04x(%s) が見つかった位置は %d[Byte]目\n", mbSingleString, pcSingleStr, result2 );
  else
    printf_s( "検索結果:   %s not found\n\n", pcSingleStr );


  //★シングルバイト文字検索。
  printf_s( "\n\n");
  printf_s( "★シングルバイト文字の初期値による検索\n");
  printf_s( "検索文字:   %c\n", SingleChr );


  //前方検索 
  pcPos = strchr( pcString, SingleChr );
  result = (int)(pcPos - pcString + 1);
  if ( pcPos != NULL )
    printf_s( "検索結果:   最初の %c が見つかった位置は %d[Byte]目\n", SingleChr, result );
  else
    printf_s( "検索結果:   %c not found\n", SingleChr );


   //後方検索 
  pcPos = strrchr( pcString, SingleChr );
  result = (int)(pcPos - pcString + 1);
  if ( pcPos != NULL )
    printf_s( "検索結果:   最後に %c が見つかった位置は %d[Byte]目\n", SingleChr, result );
  else
    printf_s( "検索結果:\t%c not found\n", SingleChr );


  //★文字列への文字列検索。
  printf_s( "\n\n");
  printf_s( "★文字列への文字列検索\n");
  printf_s( "検索文字:   %s\n", pcStr );

  pucRusultPos = _mbsstr((unsigned char*)pcString, (unsigned char*)pcStr);
  result2 = (int)(pucRusultPos - (unsigned char*)pcString + 1);
  if ( pucRusultPos != NULL )
    printf_s( "検索結果:   最初の (%s) が見つかった位置は %d[Byte]目\n", pcStr, result2 );
  else
    printf_s( "検索結果:   %s not found\n\n", pcSingleStr );  
	  return 0;
}

出力結果 等幅フォントに設定したテキストエディタに張り付けると、もう少し見やすくなります。

文字列検索:
 
      日本語文字の検索用文字列です。The quick brown dog jumps over the lazy fox
               1         2         3         4         5         6
      123456789012345678901234567890123456789012345678901234567890123456789

★マルチバイト対応検索
MultiByteString検索文字 SigngleWord:97f1(列)
f1
97
MultiByteString検索文字 SigngleWord:97f1(列)
incriment:2[Byte]

検索結果:   最初の 97f1(列) が見つかった位置は 23[Byte]目


★シングルバイト文字の初期値による検索
検索文字:   r
検索結果:   最初の r が見つかった位置は 42[Byte]目
検索結果:   最後に r が見つかった位置は 60[Byte]目


★文字列への文字列検索
検索文字:   文字列
検索結果:   最初の (文字列) が見つかった位置は 19[Byte]目

文字列全体の一致による検索の関数はstrstr関数で、***str系の関数です。他にも文字セット検索なるものはあります。

文字列ファイルへの入出力

ここまで、やってきた手法で文字列をファイルに保存することをやってみます。文字列はかならずしもテキストファイルとして保管されるものではありません。DBに格納されるもの、バイナリファイルに格納されるもの、圧縮されるもの、様々です。ここでは一般的なテキストファイルとして保管し、簡単なテキストDBとしての操作について作業します。


まずは、テキストから文字列の読み込みのサンプルをぺたぺたとしたいのですが、一応、標準の関数でサポートされている範囲の文字コードはShiftJISとUTF-8とUTF-16でVisualStudio2005からfopenやwfopen関数が拡張されて、2番目の引数ファイルオープンモード文字列に ccs=UTF-8とかccs=UTF-16LEと記載することで、ファイルポインタをつかってのストリーム入力時fgetws関数でワイド文字(UNICODE)に変換してくれます。テキストは自分で作るといいのですが、自分は以下のような簡単なテキストを文字コード別に準備したので、参考にリンクを張っておきます。なんてことないテキストファイルなので、無価値なのものです。

文字列操作サンプル用文字コード4種セット.lzh

JISやEUCといったサポートされていない文字コードのテキストファイルの場合はバイナリモードでファイルを読み込む必要があり、1バイトづつ処理をして、文字列として変換したりすることで、文字列操作を行います。変換そのものは先に紹介した文字コード変換で対応することが可能ですので、とりこんだバイナリコードをどうやって変換関数のなかに収めるかというのが課題になると思います。その例はのちほど考えるとして、まずはオーソドックスなファイルの読み込みからやってみます。

#pragma once
#include "stdafx.h"
#include <stdlib.h>
#include <string>		//std::string型定義
#include <mbstring.h>	//mbs***関数
#include <iostream>		//cpp cout etc 一般入出力関数
#include <locale>		//Locale関数
#include <tchar.h>		//TCHAR型+_tcs***関数
#include "atlstr.h"		//CString,CStringA,CStringWおよびLPTSTR,LPWSTR,LPSTR,LPCTSTR,LPCWSTR,LPCSTRの定義プリプロセッサ
#include "atlbase.h"	//同上
#include "cstringt.h"	//CStringTの定義プリプロセッサ
#include <vector>

#include <sys/timeb.h>  //Timeb型構造体

//_bstr_t型を利用するプリプロセッサ
#include "comutil.h"
#ifdef _DEBUG
#pragma comment(lib, "comsuppwd.lib")
#else
#pragma comment(lib, "comsuppw.lib")
#endif
//_bstr_t

//ICU ucnvプリプロセッサ
#include <unicode/ucnv.h>
#include <unicode/translit.h>
#include <unicode/regex.h>
#ifdef _DEBUG
//#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuind.lib")//ICU Transliterate関数を使うために必要なライブラリ 正規表現関数も
#pragma comment(lib, "icuucd.lib")//ICU ucnvを使うために必要なライブラリ
//#pragma comment(lib, "icuiod.lib")
#else
#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuind.lib")
#pragma comment(lib, "icuuc.lib")
#pragma comment(lib, "icuio.lib")
#endif


#include "UnicodeConverter.h"


using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
  _tsetlocale(LC_ALL, _T("Japanese")); //ロケールセットすると_l関数でロケール指定しなくても日本語が使われます。

	_locale_t localeJpanease;
	localeJpanease = _create_locale(LC_ALL, "Japanese");

  FILE* pfileText;
  errno_t errNo;
  size_t* psizeRuturnValue = new size_t;
  int nCloseCnt;
  wchar_t pwcStrFilePathNewTexttoBinary[] = L"C:\\...\\...\\...\\new_test_t2b.txt";//...\\...\\...には正確なパスを指定。
  wchar_t pwcStrFilePathNewUTF16LE[]      = L"C:\\...\\...\\...\\new_test_utf16b.txt";
  wchar_t pwcStrFilePathNewUTF8[]         = L"C:\\...\\...\\...\\new_test_utf8b.txt";
  wchar_t pwcStrFilePathNewUTF8BOM[]      = L"C:\\...\\...\\...\\new_test_utf8bomb.txt";
  wchar_t pwcStrFilePathShiftJIS[]        = L"C:\\...\\...\\...\\test.txt";
  wchar_t pwcStrFilePathJIS[]             = L"C:\\...\\...\\...\\test_jis.txt";
  wchar_t pwcStrFilePathEUC[]             = L"C:\\...\\...\\...\\test_euc.txt";
  wchar_t pwcStrFilePathUtf8Bom[]         = L"C:\\...\\...\\...\\test_utf8.txt";
  wchar_t pwcStrFilePathUtf8[]            = L"C:\\...\\...\\...\\test_utf8N.txt";
  wchar_t pwcStrFilePathUtf16LE[]         = L"C:\\...\\...\\...\\test_utf16LE.txt";

  wchar_t pwcStrGohan[] = L"ごはんを食べたよ。\r\n";//\r\n = CR LF = 0x0D 0x0A 
  char* pcStrStream;
  wchar_t* pwcStrStream;
  char* pcStrErr;
  wchar_t* pwcStrErr;

  pcStrStream = new char[1024];
  pwcStrStream = new wchar_t[1024];
  pcStrErr = new char[1024];
  pwcStrErr = new wchar_t[1024];

  //★★★★★★★★★★★★★★★★★★★★★★★★
  //★UTF8のBOM無しテキストの読み込み
  //★★★★★★★★★★★★★★★★★★★★★★★★

  //※読み込みバッファを1024文字にしているので、1024文字以上の行を一続きにさせたい場合は、工夫が必要。それは後で考えます。
  //※行ごとにバッファの文字数を動的に確保したい場合、
  // 一文字づつスキャンして改行コードが現れるところまでのバイト長を確保するという手法もありそう。

  fpos_t* fposPos = new fpos_t;

  printf("★UTF8のBOM無しテキストの読み込み\n");
  errNo = _wfopen_s( &pfileText, pwcStrFilePathUtf8, L"r,ccs=UTF-8");
  printf("Error?->%d\n", errNo);
  
  fgetpos(pfileText, fposPos);
  printf("fpos-> %04d\n", *fposPos); //ファイルを開いた時点でのポジションの確認。BOM無しだと0文字目。 

  //Unicodeサンプル文字 1行目
  printf("文字コードUTF-16->");
  for(int i=0; i < (int)wcslen(pwcStrGohan) + 1; i++){
    printf("%04x:",*(pwcStrGohan + i));
  }
  printf("\n");

  printf("utf8N.txtテキストファイルの中身とその他の情報出力\n");
  while(!feof(pfileText)){

    pwcStrErr = fgetws(pwcStrStream,1024,pfileText);//最終行がEOFのみの場合に失敗して1行前の値が残ることに注意。
                                                    //先頭からUTF-8→UTF16形式で文字列を取得する。
    wprintf(L"%s(%s)\n", pwcStrStream, pwcStrErr);//UTF-16のpwcStrStreamをSJIS(Locale設定)で書き出し。UTF-16のままだと文字化けする。
    
    printf("文字コードUTF-16->");
    for(int i=0 ;i < 20 ;i++){ //Unicodeに変換された文字コードを先頭20文字だけ出力。
      printf("%04x:",*(pwcStrStream + i));
    }

    fgetpos(pfileText, fposPos);
    printf("fpos-> %04d\n", *fposPos);  
  }
  printf("[EOF]");
  printf("\n");

  nCloseCnt = fclose(pfileText);
  printf("nCloseErr->%d\n", nCloseCnt);
  
  nCloseCnt = _fcloseall();
  printf("nCloseCnt->%d\n", nCloseCnt);
  printf("\n");

  //★★★★★★★★★★★★★★★★★★★★★★★★
  //★UTF8のBOM有りテキストの読み込み
  //★★★★★★★★★★★★★★★★★★★★★★★★

  printf("★UTF8のBOM有りテキストの読み込み\n");
  errNo = _wfopen_s( &pfileText, pwcStrFilePathUtf8Bom, L"r,ccs=UTF-8");
  printf("Error?->%d\n", errNo);
  
  fgetpos(pfileText, fposPos);
  printf("fpos-> %04d\n", *fposPos); //ファイルを開いた時点でのポジションの確認。BOM無しだと3(0からカウントなので実際は4byte目)。 

  printf("utf8.txtテキストファイルの中身\n");
  while(!feof(pfileText)){

    pwcStrErr = fgetws(pwcStrStream,1024,pfileText);//最終行がEOFのみの場合に失敗して1行前の値が残ることに注意。
                                                    //UTF-8(BOMはEF BB BFなので4バイト目)からUTF16形式で文字列を取得する。
                                                    //※FE FFをUTF-8でエンコードするとEF BB BF
                                                    //※FE FF = 1111 1110 1111 1111 を
                                                    //                1111     11   10  11     11   1111 を
                                                    //          1110 (XXXX) 10(XX) (XX)(YY) 10(YY) (YYYY)と割り当て
                                                    //1110 1111 1011 1011 1011 1111 = EF BB BF

    if(pwcStrErr != NULL ){                         
      wprintf(L"%s", pwcStrStream);//UTF-16LEのpwcStrStreamをSJIS(Locale設定)で書き出し。UTF-16LEのままだと文字化けする。
    }
  }
  printf("[EOF]");
  printf("\n");

  nCloseCnt = fclose(pfileText);
  printf("nCloseErr->%d\n", nCloseCnt);
  printf("\n");

  //★★★★★★★★★★★★★★★★★★★★★★★★
  //★UTF16のBOM有りテキストの読み込み
  //★★★★★★★★★★★★★★★★★★★★★★★★

  printf("★UTF16LEのBOM有りテキストの読み込み\n");
  errNo = _wfopen_s( &pfileText, pwcStrFilePathUtf16LE, L"r,ccs=UTF-16LE");
  printf("Error?->%d\n", errNo);
  
  fgetpos(pfileText, fposPos);
  printf("fpos-> %04d\n", *fposPos); //ファイルを開いた時点でのポジションの確認。BOM有りで2(0からカウントなので実際は3byte目)。 

  printf("utf8.txtテキストファイルの中身\n");
  while(!feof(pfileText)){

    pwcStrErr = fgetws(pwcStrStream,1024,pfileText);//最終行がEOFのみの場合に失敗して1行前の値が残ることに注意。
    if(pwcStrErr != NULL ){                         //UTF-16(BOMはFE FFなので3バイト目)からUTF16形式で文字列を取得する。
      wprintf(L"%s", pwcStrStream);//UTF-16のpwcStrStreamをSJIS(Locale設定)で書き出し。UTF-16のままだと文字化けする。
    }
  }
  printf("[EOF]");
  printf("\n");

  nCloseCnt = fclose(pfileText);
  printf("nCloseErr->%d\n", nCloseCnt);
  printf("\n");

  //★★★★★★★★★★★★★★★★★★★★★★★★
  //★ShiftJISテキストの読み込み
  //★★★★★★★★★★★★★★★★★★★★★★★★

  printf("★ShiftJISテキストの読み込み\n");
  errNo = _wfopen_s( &pfileText, pwcStrFilePathShiftJIS, L"r");
  printf("Error?->%d\n", errNo);
  
  fgetpos(pfileText, fposPos);
  printf("fpos-> %04d\n", *fposPos); //ファイルを開いた時点でのポジションの確認。 

  printf("test.txtテキストファイルの中身\n");
  while(!feof(pfileText)){
    
    pcStrErr = fgets(pcStrStream, 1024, pfileText);//最終行がEOFのみの場合に失敗して1行前の値が残ることに注意。
    if(pcStrErr != NULL ){                       
      printf("%s", pcStrStream);
    }
  }
  printf("[EOF]");
  printf("\n");

  nCloseCnt = fclose(pfileText);
  printf("nCloseErr->%d\n", nCloseCnt);
  printf("\n");

  //★★★★★★★★★★★★★★★★★★★★★★★★
  //★JISテキストの読み込み
  //★★★★★★★★★★★★★★★★★★★★★★★★
  
  //int型でchar配列のサイズを動的に確保ができるので、int型の範囲のファイルなら
  //一度に格納できるプログラムになっています。intの範囲を超えると動的メモリ確保で
  //失敗します。その先の工夫はまた別途考える必要があります。少し筒変換したり…。

  int nSize = 0;
  int nFeedCnt = 0;

  char* pcStrFile;
  fpos_t* pfposStartPos = new fpos_t;
  printf("★JISテキストの読み込み\n");
  errNo = _wfopen_s( &pfileText, pwcStrFilePathJIS, L"rb");
  printf("Error?->%d\n", errNo);
  
  fgetpos(pfileText, fposPos);
  printf("fpos-> %04d\n", *fposPos); //ファイルを開いた時点でのポジションの確認。 

  printf("test_jis.txtテキストファイルの中身\n");
  fgetpos(pfileText, pfposStartPos);
  while(!feof(pfileText)){
    nSize = nSize + nFeedCnt * 1024;
    nFeedCnt++;
    *psizeRuturnValue = fread(pcStrStream, sizeof(char), 1024, pfileText);//最終行がEOFのみの場合に失敗して1行前の値が残ることに注意。
      fgetpos(pfileText, fposPos);                   
      for(int i = 0; i < *fposPos; i++){
        printf("%02x:",0x000000FF & *(pcStrStream + i));
      }
  }
  printf("[EOF]");
  printf("\n");

  nSize = nSize + (int)*fposPos - ((nFeedCnt - 1) * 1024);
  fsetpos(pfileText, pfposStartPos);
  pcStrFile = new char[nSize + 1];
  while(!feof(pfileText)){
    *psizeRuturnValue = fread(pcStrFile, sizeof(char), nSize + 1, pfileText);//最終行がEOFのみの場合に失敗して1行前の値が残ることに注意。
      for(int i = 0; i < nSize; i++){
        printf("%02x:",0x000000FF & *(pcStrFile + i));
      }
  }
  
  printf("[EOF]");
  printf("\n");
  fgetpos(pfileText, fposPos);
   printf("fpos-> %04d\npReturnValue-> %04d\n", *fposPos, *psizeRuturnValue); //ファイルを開いた時点でのポジションの確認。 
  

  nCloseCnt = fclose(pfileText);
  printf("nCloseErr->%d\n", nCloseCnt);
  printf("\n");

  UConverter* ucnvTest;
  UErrorCode uerrorNum;
  int nStrSize;
  int nStrResultSize;
  nStrSize = nSize;

  std::wstring stringCnvResult(nStrSize, L'\0');

  ucnvTest = ucnv_open("iso-2022-jp", &uerrorNum);
  nStrResultSize = ucnv_toUChars(
    ucnvTest,
    &stringCnvResult[0],	stringCnvResult.size(),	// 変換先のポインタとサイズ
    &pcStrFile[0],				nStrSize,				        // 変換元のポインタとサイズ
    &uerrorNum
  ); 
  stringCnvResult.resize(nStrResultSize);
  ucnv_close(ucnvTest);
  _tprintf(_T("%s\n"), stringCnvResult.c_str());	
  printf("\n");
  return 0;
}

出力結果

★UTF8のBOM無しテキストの読み込み
Error?->0
fpos-> 0000
文字コードUTF-16LE->3054:306f:3093:3092:98df:3079:305f:3088:3002:000d:000a:0000:
utf8N.txtテキストファイルの中身とその他の情報出力
ごはんを食べたよ。
(ごはんを食べたよ。
)
文字コードUTF-16LE->3054:306f:3093:3092:98df:3079:305f:3088:3002:000a:0000:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:fpos-> 0029

(
)
文字コードUTF-16LE->000a:0000:3093:3092:98df:3079:305f:3088:3002:000a:0000:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:fpos-> 0031

(
)
文字コードUTF-16LE->000a:0000:3093:3092:98df:3079:305f:3088:3002:000a:0000:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:fpos-> 0033
それだけだよ。
(それだけだよ。
)
文字コードUTF-16LE->305d:308c:3060:3051:3060:3088:3002:000a:0000:000a:0000:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:fpos-> 0056
それだけだよ。
((null))
文字コードUTF-16LE->305d:308c:3060:3051:3060:3088:3002:000a:0000:000a:0000:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:fpos-> 0056
[EOF]
nCloseErr->0
nCloseCnt->0

★UTF8NのBOM有りテキストの読み込み
Error?->0
fpos-> 0003
utf8.txtテキストファイルの中身
ごはんを食べたよ。


それだけだよ。
[EOF]
nCloseErr->0

★UTF16LEのBOM有りテキストの読み込み
Error?->0
fpos-> 0002
utf8.txtテキストファイルの中身
ごはんを食べたよ。


それだけだよ。
[EOF]
nCloseErr->0

★ShiftJISテキストの読み込み
Error?->0
fpos-> 0000
test.txtテキストファイルの中身
ごはんを食べたよ。


それだけだよ。
[EOF]
nCloseErr->0

★JISテキストの読み込み
Error?->0
fpos-> 0000
test_jis.txtテキストファイルの中身
1b:24:42:24:34:24:4f:24:73:24:72:3f:29:24:59:24:3f:24:68:21:23:1b:28:4a:0d:0a:0d:0a:0d:0a:1b:24:42:24:3d:24:6c:24:40:24:31:24:40:24:68:21:23
:1b:28:4a:0d:0a:[EOF]
1b:24:42:24:34:24:4f:24:73:24:72:3f:29:24:59:24:3f:24:68:21:23:1b:28:4a:0d:0a:0d:0a:0d:0a:1b:24:42:24:3d:24:6c:24:40:24:31:24:40:24:68:21:23
:1b:28:4a:0d:0a:[EOF]
fpos-> 0052
pReturnValue-> 0000
nCloseErr->0

ごはんを食べたよ。


それだけだよ。

という感じです。ごはんを食べたよ それだけだよ。というサンプルの文字列に特に深い意味はありませんが、Windowsは改行コードにCR LFを使うのが一般的です。しかしながら、ファイルから取り込んできた文字列にでは\r 0x0dは無くなって\nにあたる0x0aだけが残る仕様になっています。これらの改行復帰コードを厳密に扱う場合には書き込みするときや、文字列操作時に工夫が必要になります。文字コードがどんなふうに扱われているのか、厳密にどうなっているのか調べるためにも、時々は文字コードそのものを出力して確かめるのも大事なのではないかと思います。人間様にわかる文字だけを出力していたのでは、コンピュータを正確にあやつることは難しいです。JISの変換ではファイルオープン時にバイナリで開いています。これをchar型の変数に格納して、ICUのJIS→UNICODE変換を実施しています。


テキストファイルをバイナリで読み込む場合には区切りが大事です。動的に生成できる文字列配列の大きさはint型の範囲が限界ですので、ひとつだけで扱おうとするのは無理がありますし、int型くらい大きいサイズのメモリ確保は無理があります。もう少し小さい単位で文字列を扱える工夫が必要になります。JISテキストファイルなら改行コードまでの大きさくらいが良いと思います。文字列の中に改行コードが見つかるまで文字数を計算して、それでメモリ確保をし、JIS文字列を変換して、作業用の文字列配列に格納するといった、そういう仕組みにするだけのことです。このプログラムはテキストエディタやファイルローダとしての役割を担うものではないので、そこまでの具体的な拡張には挑戦しませんでした。実際の組み込みでは、プログラムが扱うデータの大きさにちょうどよい区切りを準備することになると思います。 まぁこのままでもかなり大きさまで扱えるので、コンソールで遊ぶ程度ならこれでもいいんではないでしょうか?それよか、メモリ確保がうまくいかない場合の例外処理とか追加しますかね。そのあたりの説明はまた別の項目になるかと思います。


ファイル読み込み時に文字コードセットが判明しているサンプルプログラムのようなケースはプログラムが簡単でしたが、もともとのファイル形式を自動判別して読み込むプログラムに対応しようとすると、またひとつ大変さが増します。自動判別ができないのはutf-8で書かれた英字だけのファイルを開いた場合で、ShiftJISとするのが最適なのか?UTF-8として開くのが最適なのかは、ユーザにあらかじめ決めてもらった方が良いかもしれません。一度でも日本語が使われていれば、おのずと判定ができるわけですが、すべての文字をチェックして英字以外の範囲の文字コードを見つけて、それが、どの文字コードなのかを判別する仕組みが必要です。


おもしろそうですが、文字コードに詳しくないので、自分はまだその手法をしりません。また今度、考えてみようと思います。文字化けしてるコードをみて、あーこれはもともとはこのコードだったんだろうなという感覚はあるんですが、それと似たような法則があるんでしょうね。結局、この判定もICUでできちゃうんですけどね。これも時間があればやってみたいと思います。


次はファイル出力にあたる書き込みですね。

  //JISからUTF-16LEへの変換処理

  UConverter* ucnvTest;
  UErrorCode uerrorNum;
  int nStrSize;
  int nStrResultSize;
  char pcStrUTF16LEBOM[] = "\xFF\xFE";//\xエスケープシーケンスはx以降の16進数とした文字コードを示すもの。1バイトの文字コード指定で利用できます。
  char pcStrUTF8BOM[] = "\xEF\xBB\xBF";
  nStrSize = nSize;

  std::wstring stringCnvResult(nStrSize, L'\0');

  ucnvTest = ucnv_open("iso-2022-jp", &uerrorNum);
  nStrResultSize = ucnv_toUChars(
    ucnvTest,
    &stringCnvResult[0],	stringCnvResult.size(),	// 変換先のポインタとサイズ
    &pcStrFile[0],				nStrSize,				        // 変換元のポインタとサイズ
    &uerrorNum
  ); 
  stringCnvResult.resize(nStrResultSize);
  ucnv_close(ucnvTest);
  _tprintf(_T("%s\n"), stringCnvResult.c_str());	
  printf("\n");

  //★★★★★★★★★★★★★★★★★★★★★★★★
  //★UTF16LEのTextモードでの書き込み
  //★★★★★★★★★★★★★★★★★★★★★★★★

  //\n 0x0Aが \r\nに変換されるため、Binaryモードで読み込んだ\r\nを保持した文字列から
  //変換した文字列をテキストモードで書き込むと\r\r\nのように2重復帰が書き込まれる。
  //テキストエディタでは\rひとつでも、改行されるため \r と \r\nで2つ改行になる。

  errNo = _wfopen_s( &pfileText, pwcStrFilePathNewTexttoBinary, L"w,ccs=UTF-16LE");
  fputws(stringCnvResult.c_str(), pfileText);
  fclose(pfileText);

  //★★★★★★★★★★★★★★★★★★★★★★★★
  //★UTF16LEのBinaryモードテキストの書き込み
  //★★★★★★★★★★★★★★★★★★★★★★★★

  errNo = _wfopen_s( &pfileText, pwcStrFilePathNewUTF16LE, L"wb");
  fputs(pcStrUTF16LEBOM,pfileText);
  fputws(stringCnvResult.c_str(), pfileText);
  fclose(pfileText);

  //UnicodeUTF-16LEのutf-8への変換処理
  ucnvTest = ucnv_open("UTF-8", &uerrorNum);

  nStrSize = stringCnvResult.size();
  std::string stringCnvResult3(ucnv_getMaxCharSize(ucnvTest) * nStrSize , L'\0');


  nStrResultSize = ucnv_fromUChars(
    ucnvTest,
    &stringCnvResult3[0],	stringCnvResult3.size(),	// 変換先のポインタとサイズ
    &stringCnvResult[0],				nStrSize,					// 変換元のポインタとサイズ
    &uerrorNum
  ); 
  stringCnvResult3.resize(nStrResultSize);
  ucnv_close(ucnvTest);
  printf("%s\n", stringCnvResult3.c_str());	

  //★★★★★★★★★★★★★★★★★★★★★★★★
  //★UTF8のBOM無しテキストの書き込み
  //★★★★★★★★★★★★★★★★★★★★★★★★

  errNo = _wfopen_s( &pfileText, pwcStrFilePathNewUTF8, L"wb");
  //fputs(pcStrUTF8BOM,pfileText);
  fputs(stringCnvResult3.c_str(), pfileText);
  fclose(pfileText);

  //★★★★★★★★★★★★★★★★★★★★★★★★
  //★UTF8のBOM有りテキストの書き込み
  //★★★★★★★★★★★★★★★★★★★★★★★★

  errNo = _wfopen_s( &pfileText, pwcStrFilePathNewUTF8BOM, L"wb");
  fputs(pcStrUTF8BOM,pfileText);
  fputs(stringCnvResult3.c_str(), pfileText);
  fclose(pfileText);

先述の読み込み処理に続けてのプログラムになります。ファイルオープンモードでwを指定して、fputs関数でchar型の書き込み、fputwsでwchar_t型の書き込みをするような形式です。モードaで追記なのでファイル末尾から追記されます。ファイルの先頭に追加するときはBOMコードの後の所にファイルポジションをfsetpos関数で移動してからfputws/fputsで書き込むと良いでしょう。新規のファイル作成となるモードwのバイナリモードwbでは、BOMコードを付与しなければならないケースも出てきます。fputsでBOMコードを保持した文字列を引数にすると良いです。bを付けないテキストモードでの書き込みでは、ccs=UTF-8やccs=UTF-16LEを指定すると自動でBOMコードを追記してくれますので、そのままUTF-16ならwchar_t型の文字列を書き出せばよいです。但し、バイナリモードで読み込んだ文字列は\r\nがそのまま文字列になっているので、書き出し時に後ろの\nが\r\nに変換され、\r\r\nになってしまうので、バイナリモードで読み込んだ文字をテキストモードで書き出すのはやめた方がいいです。どうしても相互乗り換えするのであれば、改行コードの置換処理が必要です。UTF-8のテキストモード書き込みでもユニコードのUTF-16LE形式で書き込むことでUTF-8に変換されますので、プログラム内部ではUnicodeで文字列を扱った方が効率が良いと思います。テキストモードでサポートしない文字コードのような特殊な形式に変換する場合はバイナリモードで文字列を読み込んで、変換処理を実施し、char型に格納した文字コードをバイナリモードで書き出すと良いでしょう。サンプルプログラムではテキストモードで読み込んだJIS文字をUnicodeに変換しUTF-16テキストモードで書き込んだ失敗例をプログラムにしてあります。ファイルオープンモードをうまく使いこなすことと、Unicode以外の文字列はchar型でバイナリとして扱うところがポイントです。変換処理はICUに任せればかなり柔軟な入出力処理が作れると思います。


ファイルオープンモードについてですが、r、w、a、rb、wb、ab の他にもr+ のように+記号を付けるパターンがあります。この場合、r+ 既存ファイルに読み込み上書き、w+ 新規ファイルで読み書き、a+ 既存ファイルに読み追記ができます。cをつけるとflush関数による反映ですべての操作が確定しファイルに反映するモードになります。通常はcではないモードのnが規定値になっています。bを指定しない場合も実際はtが規定値として扱われています。他にもディスクにファイルを残さずメモリ上だけでファイルの操作をするためにT(ファイル生成されない)やD(ファイルポインタを閉じると削除されるファイル)といった一時ファイル指定もありますし、シーケンシャルアクセス最適化のSにランダムアクセス最適化のR、子プロセスに継承しないNといった非常に難しいコアなオプションもあります。シーケンシャルやランダムアクセスについては記録するディスクの仕組みにもよりますが、通常はシーケンシャルの方が読み取り効率が良いはずですが、ランダムに配置された方が待ち時間が少なくなるというケースもあります。大容量・大量のファイルを扱う場合によく調べて選択するとよいかもしれません。Nは違うプログラム(子プロセス)からファイルポインタを2重に参照させないというようなオプションかと思います。やってみたことないので、具体的な例はここでは示せません。やはり経験が少ないと細かいところまでは説明できないですね。無念。


ここの長ったらしい文字列処理の説明の総括的なプログラミングになっているかと思います。全部理解してこそ、やりきれる処理です。文字列処理のプログラムを普段作らない自分でもこの程度なら理解しておきたいものです。ここのサンプルは直線的なプログラムで無駄の多いサンプルです。みなさんはもっともっと効率のいい文字列処理をやって下さい。ここでは関数の引数や、クラスの考え方をあまり知らなくても、文字列処理ができるように、あまり多くの技術を使わないそういう無駄なサンプルになっていますが、上から順番に処理していくだけなので、処理の順番は理解しやすいようになっているのではないかと自負するところです。あとは文字コードの自動判定くらいを触れて、文字列処理に関するこの記事は終了にしたいなぁと思います。誰も読まないかもしれないのに、よくこんなにウダウダと長ったらしい文章を書いたなぁ。恐るべしだ。でも、こういううだうだした説明ともっと若いときに出会っていたら、もっと手早く理解できたのになぁと自分は思います。この長い文書が誰かの役に立っていたなら幸せなことです。


ICUによる文字コード自動判定のプログラムは以下のとおりです。

#pragma once
#include "stdafx.h"
#include <stdlib.h>
#include <string>      //std::string型定義
#include <mbstring.h>  //mbs***関数
#include <iostream>    //cpp cout etc 一般入出力関数
#include <locale>      //Locale関数
#include <tchar.h>     //TCHAR型+_tcs***関数
#include "atlstr.h"    //CString,CStringA,CStringWおよびLPTSTR,LPWSTR,LPSTR,LPCTSTR,LPCWSTR,LPCSTRの定義プリプロセッサ
#include "atlbase.h"   //同上
#include "cstringt.h"  //CStringTの定義プリプロセッサ
#include <vector>      //Vector型テンプレートクラス

#include <sys/timeb.h> //Timeb型構造体

//_bstr_t型を利用するプリプロセッサ(ライブラリはデバッグモードとリリースモード個別設定)
#include "comutil.h"

#ifdef _DEBUG
#pragma comment(lib, "comsuppwd.lib")
#else
#pragma comment(lib, "comsuppw.lib")
#endif
//_bstr_t _End

//ICU ucnvプリプロセッサ(ライブラリはデバッグモードとリリースモード個別設定)
#include <unicode/ucnv.h>     //ucnv文字コード変換ライブラリヘッダ
#include <unicode/translit.h> //文字変換ライブラリヘッダ
#include <unicode/regex.h>    //正規表現
#include <unicode/ucsdet.h>   //文字コード判定

#ifdef _DEBUG
//#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuind.lib")  //ICU Transliterate関数を使うために必要なライブラリ 正規表現関数も
#pragma comment(lib, "icuucd.lib")  //ICU ucnvを使うために必要なライブラリ
//#pragma comment(lib, "icuiod.lib")
#else
//#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuind.lib")  //ICU Transliterate関数を使うために必要なライブラリ 正規表現関数も
#pragma comment(lib, "icuuc.lib")   //ICU ucnvを使うために必要なライブラリ
//#pragma comment(lib, "icuio.lib")
#endif
//ICU ucnv _End

#include "UnicodeConverter.h"

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
  _tsetlocale(LC_ALL, _T("Japanese")); //ロケールセットすると_l関数でロケール指定しなくても日本語が使われます。

	_locale_t localeJpanease;
	localeJpanease = _create_locale(LC_ALL, "Japanese");

  FILE* pfileText;
  fpos_t* fposPos = new fpos_t;
  errno_t errNo;
  size_t* psizeRuturnValue = new size_t;
  
  wchar_t pwcStrFilePath[]             = L"C:\\Users\\Administrator\\Documents\\マイ インフォメーション\\ファイル入出力\\test_utf16LE.txt";
  char* pcStrStream;
  pcStrStream = new char[1024];
  int nCloseResult = 0;
  //★★★★★★★★★★★★★★★★★★★★★★★★
  //★テキストの読み込み
  //★★★★★★★★★★★★★★★★★★★★★★★★
  
  //int型でchar配列のサイズを動的に確保ができるので、int型の範囲のファイルなら
  //一度に格納できるプログラムになっています。intの範囲を超えると動的メモリ確保で
  //失敗します。その先の工夫はまた別途考える必要があります。少し筒変換したり…。

  int nSize = 0;
  int nFeedCnt = 0;

  char* pcStrFile;
  fpos_t* pfposStartPos = new fpos_t;
  printf("★テキストの読み込み\n");
  errNo = _wfopen_s( &pfileText, pwcStrFilePath, L"rb");
  printf("Error?->%d\n", errNo);
  
  fgetpos(pfileText, fposPos);
  printf("fpos-> %04d\n", *fposPos); //ファイルを開いた時点でのポジションの確認。 

  printf("test.txtテキストファイルの中身\n");
  fgetpos(pfileText, pfposStartPos);
  while(!feof(pfileText)){
    nSize = nSize + nFeedCnt * 1024;
    nFeedCnt++;
    *psizeRuturnValue = fread(pcStrStream, sizeof(char), 1024, pfileText);//最終行がEOFのみの場合に失敗して1行前の値が残ることに注意。
      fgetpos(pfileText, fposPos);                   
      for(int i = 0; i < *fposPos; i++){
        printf("%02x:",0x000000FF & *(pcStrStream + i));
      }
  }
  printf("[EOF]");
  printf("\n");

  nSize = nSize + (int)*fposPos - ((nFeedCnt - 1) * 1024);
  fsetpos(pfileText, pfposStartPos);
  pcStrFile = new char[nSize + 1];
  while(!feof(pfileText)){
    *psizeRuturnValue = fread(pcStrFile, sizeof(char), nSize + 1, pfileText);//最終行がEOFのみの場合に失敗して1行前の値が残ることに注意。
      for(int i = 0; i < nSize; i++){
        printf("%02x:",0x000000FF & *(pcStrFile + i));
      }
  }
  
  printf("[EOF]");
  printf("\n");
  fgetpos(pfileText, fposPos);
   printf("fpos-> %04d\npReturnValue-> %04d\n", *fposPos, *psizeRuturnValue); //ファイルを開いた時点でのポジションの確認。 
  

  nCloseResult = fclose(pfileText);
  printf("nCloseErr->%d\n", nCloseResult);
  printf("\n");

  UConverter* ucnvTest;
  UErrorCode uerrorNum = U_ZERO_ERROR;
  int nStrSize;
  int nStrResultSize;
  char pcStrUTF16LEBOM[] = "\xFF\xFE";
  char pcStrUTF8BOM[] = "\xEF\xBB\xBF";
  nStrSize = nSize;

  UCharsetDetector* pucsetdetecDetector = ucsdet_open(&uerrorNum);
  ucsdet_setText(pucsetdetecDetector, pcStrFile, nSize, &uerrorNum);

  int32_t int32FindCharSetCnt = 0;
  const UCharsetMatch* pucsetmatchStrCode = ucsdet_detect(pucsetdetecDetector, &uerrorNum);

  const char* cpcDetectStrCodeName;
  const char* cpcDetectStrCodeLanguage;
  cpcDetectStrCodeName = ucsdet_getName(pucsetmatchStrCode, &uerrorNum);//文字コード名 ICUの文字コードセット名に適した形式。
  cpcDetectStrCodeLanguage = ucsdet_getLanguage(pucsetmatchStrCode, &uerrorNum);

  printf("★ICU Code Detectorによるテキストの文字コード自動判定\n");
  printf("CodeName->%s\n",cpcDetectStrCodeName);
  printf("CodeLang->%s\n",cpcDetectStrCodeLanguage);//RFC3066コードを取得できる。

  //Shift_JISからUTF-16LEへの変換処理

  //先頭がBOMコードなら変換する文字の先頭アドレスをBOMコードの長さを割り出し、nShiftByteに記録。
  int nShiftByte = 0;
  if(strlen(pcStrFile) >= 3){
    if(strncmp(pcStrFile,pcStrUTF8BOM,3) == 0){
      nShiftByte = 3;
    }
  }
  if(strlen(pcStrFile) >= 2){
    if(strncmp(pcStrFile,pcStrUTF16LEBOM,2) == 0){
      nShiftByte = 2;
    }
  }

  std::wstring stringCnvResult(nStrSize, L'\0');

  ucnvTest = ucnv_open(cpcDetectStrCodeName, &uerrorNum);//変換する文字コードは自動判定で取得した文字コード。
  nStrResultSize = ucnv_toUChars(
    ucnvTest,
    &stringCnvResult[0],	stringCnvResult.size(),	           // 変換先のポインタとサイズ
    &pcStrFile[0 + nShiftByte],				nStrSize - nShiftByte, // 変換元のポインタとサイズ BOMサイズがnShiftByteに格納されている。
    &uerrorNum
  ); 
  stringCnvResult.resize(nStrResultSize);
  ucnv_close(ucnvTest);
  ucsdet_close(pucsetdetecDetector);
  _tprintf(_T("%s[EOF]"), stringCnvResult.c_str());	
  printf("\n");
  return 0;
}

出力結果 utf16LEのテキストを指定した場合。

★テキストの読み込み
Error?->0
fpos-> 0000
test.txtテキストファイルの中身
ff:fe:54:30:6f:30:93:30:92:30:df:98:79:30:5f:30:88:30:02:30:0d:00:0a:00:0d:00:0a:00:0d:00:0a:00:5d:30:8c:30:60:30:51:30:60:30:88:30:02:30:0d:00:0a:00:[EOF]
ff:fe:54:30:6f:30:93:30:92:30:df:98:79:30:5f:30:88:30:02:30:0d:00:0a:00:0d:00:0a:00:0d:00:0a:00:5d:30:8c:30:60:30:51:30:60:30:88:30:02:30:0d:00:0a:00:[EOF]
fpos-> 0050
pReturnValue-> 0000
nCloseErr->0

★ICU Code Detectorによるテキストの文字コード自動判定
CodeName->UTF-16LE
CodeLang->
ごはんを食べたよ。


それだけだよ。
[EOF]

出力結果 utf8のテキストを指定した場合。

★テキストの読み込み
Error?->0
fpos-> 0000
test.txtテキストファイルの中身
ef:bb:bf:e3:81:94:e3:81:af:e3:82:93:e3:82:92:e9:a3:9f:e3:81:b9:e3:81:9f:e3:82:88:e3:80:82:0d:0a:0d:0a:0d:0a:e3:81:9d:e3:82:8c:e3:81:a0:e3:81:91:e3:81:a0:e3:82:88:e3:80:82:0d:0a:[EOF]
ef:bb:bf:e3:81:94:e3:81:af:e3:82:93:e3:82:92:e9:a3:9f:e3:81:b9:e3:81:9f:e3:82:88:e3:80:82:0d:0a:0d:0a:0d:0a:e3:81:9d:e3:82:8c:e3:81:a0:e3:81:91:e3:81:a0:e3:82:88:e3:80:82:0d:0a:[EOF]
fpos-> 0059
pReturnValue-> 0000
nCloseErr->0

★ICU Code Detectorによるテキストの文字コード自動判定
CodeName->UTF-8
CodeLang->
ごはんを食べたよ。


それだけだよ。
[EOF]

出力結果 JISのテキストを指定した場合。

★テキストの読み込み
Error?->0
fpos-> 0000
test.txtテキストファイルの中身
1b:24:42:24:34:24:4f:24:73:24:72:3f:29:24:59:24:3f:24:68:21:23:1b:28:4a:0d:0a:0d:0a:0d:0a:1b:24:42:24:3d:24:6c:24:40:24:31:24:40:24:68:21:23:1b:28:4a:0d:0a:[EOF]
1b:24:42:24:34:24:4f:24:73:24:72:3f:29:24:59:24:3f:24:68:21:23:1b:28:4a:0d:0a:0d:0a:0d:0a:1b:24:42:24:3d:24:6c:24:40:24:31:24:40:24:68:21:23:1b:28:4a:0d:0a:[EOF]
fpos-> 0052
pReturnValue-> 0000
nCloseErr->0

★ICU Code Detectorによるテキストの文字コード自動判定
CodeName->ISO-2022-JP
CodeLang->
ごはんを食べたよ。


それだけだよ。
[EOF]

出力結果 ShiftJISのテキストを指定した場合。

★テキストの読み込み
Error?->0
fpos-> 0000
test.txtテキストファイルの中身
82:b2:82:cd:82:f1:82:f0:90:48:82:d7:82:bd:82:e6:81:42:0d:0a:0d:0a:0d:0a:82:bb:82:ea:82:be:82:af:82:be:82:e6:81:42:0d:0a:[EOF]
82:b2:82:cd:82:f1:82:f0:90:48:82:d7:82:bd:82:e6:81:42:0d:0a:0d:0a:0d:0a:82:bb:82:ea:82:be:82:af:82:be:82:e6:81:42:0d:0a:[EOF]
fpos-> 0040
pReturnValue-> 0000
nCloseErr->0

★ICU Code Detectorによるテキストの文字コード自動判定
CodeName->Shift_JIS
CodeLang->ja
ごはんを食べたよ。


それだけだよ。
[EOF]

とこのような感じになります。書き出すときには文字コード別にBOMを付与する処理が必要になりますが、自動判別によって、かなりたくさんの文字コードのテキストファイルを自動で読み込むことができるようになります。やや便利なコンソールアプリになったのかもしれません。そんな得体の知れないテキストファイルが転がっているかどうかは知りませんが…。BOMコードの種類にはUTF-16BEもあるので、このあたりにも対応したプログラムにするともっといいかもしれませんね。 今回のサンプルでは完璧な変換をしてくれましたが、ややこしい文字構成ばかりのテキストファイルになってくると間違えることもあります。そのためICUでは文字コード一致率みたいなランキングを作ってくれる関数もありまして、ucsdet_detect関数の代わりにucsdet_detectAllという関数で UCharsetMatch**型を戻り値にするものがあります。ポインタのポインタってことは、配列ってことになります。ucsdete_getName関数で、引数のその配列の要素を渡せば、それぞれの文字コード名が一覧されます。具体的には以下のような要領になります。

  int32_t int32FindCharSetCnt = 0;
  int32_t int32Confidence = 0;
  const UCharsetMatch* pucsetmatchStrCode = ucsdet_detect(pucsetdetecDetector, &uerrorNum);
  const UCharsetMatch** ppucsetmatchStrCode = ucsdet_detectAll(pucsetdetecDetector, &int32FindCharSetCnt, &uerrorNum);

  const char* cpcDetectStrCodeName;
  const char* cpcDetectStrCodeLanguage;

  for(int i = 0; i < int32FindCharSetCnt; i++){
    int32Confidence = ucsdet_getConfidence(ppucsetmatchStrCode[i], &uerrorNum);0100で表される信頼値
    cpcDetectStrCodeName = ucsdet_getName(ppucsetmatchStrCode[i], &uerrorNum);//文字コード名 ICUの文字コードセット名に適した形式。
    cpcDetectStrCodeLanguage = ucsdet_getLanguage(ppucsetmatchStrCode[i], &uerrorNum);    
    printf("CodeConf->%d\n",int32Confidence);
    printf("CodeName->%s\n",cpcDetectStrCodeName);
    printf("CodeLang->%s\n",cpcDetectStrCodeLanguage);//RFC3066コードを取得できる。
  }

のようにするとConfidenceの値が大きいほど一致率が高いものとして確認ができます。全部を一覧するにはfor文で0~int32FindCharSetCnt -1までの要素を確認するとよいです。 出力結果 UTF-16LEのファイル。

CodeConf->100
CodeName->UTF-16LE
CodeLang->
CodeConf->23
CodeName->windows-1252
CodeLang->es
CodeConf->23
CodeName->windows-1250
CodeLang->cs
CodeConf->10
CodeName->UTF-16BE
CodeLang->

となります。

文字列と数値の変換

実際のアプリケーションでは、数値の入力を行う部分でも、文字コードとして入力がなされ、各種のエディタや、エディットコントロール、入力画面からは文字としてテキストから渡されます。このように取り扱われる文字列としての数値を、プログラミング言語の数を扱う型に格納する技術が必要になります。

atoi、atol、atofのような標準関数で簡単に文字列を数値に変換することができます。逆に数値を文字列に変換する関数もitoaやltoa、ftoaのように準備されていますが非標準関数となっています。ほぼ標準で準備されています。同じことがprintf関数のような関数でva_listという可変長引数の型を引数とするvsprintf(vscprintf:文字数カウント用)関数あるい通常のsprintf(scprintf:文字数カウント)関数を使って実現できます。ここでは、使い方のややこしいvsprintfを使ってみました。数値の文字列化に関してはitoaなどの関数よりも書式設定ができる分、応用が利くので、自分ならこういうものを活用します。atoiのような関数はC++ではstd::atoiのような名前空間の中に定義されています。使い方は同じです。

では、以下がサンプルになります。

#pragma once
#include "stdafx.h"
#include <stdlib.h>
#include <string>		//std::string型定義
#include <mbstring.h>	//mbs***関数
#include <iostream>		//cpp cout etc 一般入出力関数
#include <locale>		//Locale関数
#include <tchar.h>		//TCHAR型+_tcs***関数
#include "atlstr.h"		//CString,CStringA,CStringWおよびLPTSTR,LPWSTR,LPSTR,LPCTSTR,LPCWSTR,LPCSTRの定義プリプロセッサ
#include "atlbase.h"	//同上
#include "cstringt.h"	//CStringTの定義プリプロセッサ
#include <vector>


//_bstr_t型を利用するプリプロセッサ
#include "comutil.h"
#ifdef _DEBUG
#pragma comment(lib, "comsuppwd.lib")
#else
#pragma comment(lib, "comsuppw.lib")
#endif
//_bstr_t

//ICU ucnvプリプロセッサ
#include <unicode/ucnv.h>
#include <unicode/translit.h>
#include <unicode/regex.h>
#ifdef _DEBUG
//#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuind.lib")//ICU Transliterate関数を使うために必要なライブラリ 正規表現関数も
#pragma comment(lib, "icuucd.lib")//ICU ucnvを使うために必要なライブラリ
//#pragma comment(lib, "icuiod.lib")
#else
#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuind.lib")
#pragma comment(lib, "icuuc.lib")
#pragma comment(lib, "icuio.lib")
#endif


#include "UnicodeConverter.h"


using namespace std;
int getvscprintflen(char* format, ...){
    va_list args;
    int     len;

	//可変長引数をargsに格納。引数formatの次から全てをva_list型のargsへ設定
	//引数文字列ポインタをformatの次に合わせる関数。
    va_start( args, format ); 
    len = _vscprintf( format, args );
    return len;
}

void getvsprintf(char* str, char* format, ...){
    va_list args;
    int     len;

    va_start( args, format );
    len = _vscprintf( format, args );
    vsprintf_s( str, len + 1, format, args );
}
int _tmain(int argc, _TCHAR* argv[])
{
	char pcStrInt[]="-8192";
	const char pcStrFloat[]="-3.14";
	int nInteger;
	long lLong;
	double dDouble;
	char* pcStrNumeric;
	
	int nLen;
	nInteger = atoi(pcStrFloat);
	lLong    = atol(pcStrFloat);
	dDouble  = atof(pcStrInt);

	printf("★文字列の数値化 型違いの場合\n");
	printf("nInteger->%d\n",nInteger);
	printf("lLong   ->%d\n",lLong);
	printf("dDouble ->%f\n",dDouble);
	printf("\n");

	nInteger = atoi(pcStrInt);
	lLong    = atol(pcStrInt);
	dDouble  = atof(pcStrFloat);

	printf("★文字列の数値化 型一致の場合\n");
	printf("nInteger->%d\n",nInteger);
	printf("lLong   ->%d\n",lLong);
	printf("dDouble ->%f\n",dDouble);
	printf("\n");


	//文字列長を取得する
	nLen = getvscprintflen("%3.2f", dDouble );

	pcStrNumeric = new char[nLen + 1];

	//文字列を出力※同じ引数を2回与えているのは冗長なので
	//pcStrNumericをメンバ関数にしたクラスを作ると一度にまとめれる。
	getvsprintf( pcStrNumeric, "%3.2f", dDouble ); 

	printf("★数値を文字列 vsprintf関数を利用する場合\n");
	printf( "%s\n", pcStrNumeric);
	
	delete[] pcStrNumeric;
  return 0;
}

出力結果

★文字列の数値化 型違いの場合
nInteger->-3
lLong   ->-3
dDouble ->-8192.000000

★文字列の数値化 型一致の場合
nInteger->-8192
lLong   ->-8192
dDouble ->-3.140000

★数値を文字列 vsprintf関数を利用する場合
-3.14

文字列と日付・日時・時間の変換

先述の数値の問題と同様、日付・日時・時間も文字列として与えられる場合があり、時制を扱う型との相互変換を行う技術も必要になります。


時間処理については、また別の場所で考えたいと思いますが、自分が思うところによると、時間処理には、カレンダー日付を伴う日時の概念と、カレンダー日付を考慮しない、時間の概念の2つがあると思います。時間だけの概念ならば24時間を超えて、1週間=168時間、そして1年=8760時間(うるう年の1年は8784時間ですが…)という具合に表現できると思います。この時間の概念に24時間ごとに1日と数える方式として、1.5日(一日半)にあたる36時間だと「1日と12時間」という表現です。日にちを小数点で表すのは、あまり便利ではありませんが、概念としては存在していて、日付単位にすることもありますし、会話の中では具体的な時間を0.5日割り算として、余りを切り捨てる方式が使われると思います。さらには7日毎(168時間)に1週間として表し、「1週間と1日と12時間」の表現や時間を切り捨てる方式や、日付部分を1.5日と表現する方法。そして、ここから先はうるう年や月あたりに日数の関係であいまいな表現になりますが31日or30日で1カ月と表現する方式として「(およそ)1か月と23日」のような表現に365日毎に1年とした「(およそ)1年と(およそ)3カ月と1週間と2日と10時間」のような表現。大きな単位だけを採用して、小さいものを切り捨てるという概念。ここまでは時間についてでしたが更に細かいところでは時間と分と秒でも表現できて、1時間は60分ですが1時間以上の領域も分で表現し続ける方式として、2時間を120分、24時間を1440分と表すこともあります。1週間を超えるところまで分で表すと10080分でこの先も分で表すケースは人間の感覚による理解を超える表現になってくると思います。同じことを秒でも実施して、1分は60秒の領域を超えて1時間を3600秒そして1日は86400秒。秒の場合は1時間から2時間を超えたあたりで、秒での表現の感覚理解の限界を感じます。ここまでに述べた大きな単位と組み合わせて使う場合には分や秒は59分や59秒といった範囲以内で利用されます。長くなりましたが、こういったものがカレンダー日付を考慮しない時間という概念になると思います。1年や1カ月がどこの1か月か特定されないのが日常の1年や1カ月という表現になりますので、このあたりの表現は人間にわかりやすく説明するあいまいな表現としてだけ存在すると考えていいと思います。


上記のカレンダー日付を考慮しない時間に加えて、カレンダー日付を考慮した時間の概念が存在し、これらを短い言葉で表現するなら非暦時間、暦時間と表したいと思います。暦時間の差分を考えることで非暦時間がうまれ、暦時間と非暦時間を結合させる場合には、かならず加減算して、暦時間に変化するということになります。計算機の暦時間の概念は、定義された基準時間を起点に秒単位で管理されていること、その起点が1970年1月1日00時00分00秒となっていて、世界協定時刻UTCとよばれる表現で表されていると理解しておくと良いです。そして、時刻をカウントする変数のbit数が32bitなら2038年までカウントできて、64bitなら3000億年までカウントできる仕組みです。これは個人的な希望になりますが、これからプログラムをする人は64bitの時間変数を扱って欲しいです。作成するプログラムが経済活動や、生活の基盤、生命活動を支えるものであれば尚更です。2038年は、執筆時点からあと24年でやってくるのです。経済的な損失、人的被害、労力を生まないために。各種コンパイラ提供元はそろそろ32bit時間変数のコンパイルをワーニングや使用宣言未定義エラーとかにする工夫を実施しても良いと思うのです。


これが時間処理の全般になると思います。この暦時間と非暦時間の特殊ケースとして1秒よりも小さい時間であるミリ秒の存在があるといったところでしょうか?そして、文字列として時間を表現する場合には国際社会を含めたルールに基づいて出力します。まずは標準時間からの差が最も重要な概念になります。日本なら+9時間です。あまり日本では関係の無い概念ですがサマータイムという表現もあります。グローバルな展開をするプログラムなら重要な概念かもしれません。相手の時間を思いやる必要のある双方向通信アプリとかなら、なおさら重要です。そして、曜日、午前、午後、24時間表現、表現習慣も国、地域、組織、個人によって異なります。ここでは、日付処理は別の場で考えるとして、システム時間から得られた時刻を文字列にする方法、網羅はできないですが、時刻文字列をシステム時間変数に設定し、歴時間変数として扱う方法について確認したいと思います。この日付の表現に関しても、文字コード変換でお世話になったICUはかなり手広くサポートしています。国際的な文字の変換処理に関する部分の処理系を強くしていっている印象です。たしかに個人では抱えきれないほど国際的な表現は複雑ですので、特化した組織があるというのは、ありがたいことです。

#pragma once
#include "stdafx.h"
#include <stdlib.h>
#include <string>		//std::string型定義
#include <mbstring.h>	//mbs***関数
#include <iostream>		//cpp cout etc 一般入出力関数
#include <locale>		//Locale関数
#include <tchar.h>		//TCHAR型+_tcs***関数
#include "atlstr.h"		//CString,CStringA,CStringWおよびLPTSTR,LPWSTR,LPSTR,LPCTSTR,LPCWSTR,LPCSTRの定義プリプロセッサ
#include "atlbase.h"	//同上
#include "cstringt.h"	//CStringTの定義プリプロセッサ
#include <vector>


//_bstr_t型を利用するプリプロセッサ
#include "comutil.h"
#ifdef _DEBUG
#pragma comment(lib, "comsuppwd.lib")
#else
#pragma comment(lib, "comsuppw.lib")
#endif
//_bstr_t

//ICU ucnvプリプロセッサ
#include <unicode/ucnv.h>
#include <unicode/translit.h>
#include <unicode/regex.h>
#ifdef _DEBUG
//#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuind.lib")//ICU Transliterate関数を使うために必要なライブラリ 正規表現関数も
#pragma comment(lib, "icuucd.lib")//ICU ucnvを使うために必要なライブラリ
//#pragma comment(lib, "icuiod.lib")
#else
#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuind.lib")
#pragma comment(lib, "icuuc.lib")
#pragma comment(lib, "icuio.lib")
#endif


#include "UnicodeConverter.h"


using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
    //_tsetlocale(LC_ALL, _T("Japanese")); ロケールセットすると_l関数でロケール指定しなくても日本語が使われます。

	_locale_t localeJpanease;
	localeJpanease = _create_locale(LC_ALL, "Japanese");

	//日付や時刻を取り込む文字列の長さは大きくないため、一時的な文字列確保はこのような固定長の配列で問題はないと思います。
	//それでも動的に文字列長を決定してメモリを確保したいというプログラムの効率を無視する場合には、自作しなければいけない関数が増えるのかと思います。
	char tmpbuf[128], timebuf[26], ampm[] = "AM";
    time_t ltime;
    struct _timeb structtimebTime;
    struct tm today, gmt, xmas = { 0, 0, 12, 25, 11, 93 };//秒, 分, 時間, 日, 月(0~11), 年(1900年基準)
    errno_t err;

	
	//__timeb64型構造体変数のアドレスを引数とする_ftime64_s関数でtstructに現在時刻を設定。
	_ftime_s( &structtimebTime ); // _ftimeはC4996
    //

	//環境変数TZに設定されたタイムゾーンにしたがってグローバル変数_daylight(夏時間設定)、
	//_timezone(秒時差)、_tzname配列(要素1->タイムゾーン名 要素2->夏時間タイムゾーン名)を設定。
    _tzset();


    //strtimeは10文字で現在時刻を引数の文字列変数に格納する関数。
    _strtime_s( tmpbuf, 128 );
    printf( "OS 時間:\t\t\t\t%s\n", tmpbuf );
    //strdateは9文字で現在日付を引数の文字列変数に格納する関数。
    _strdate_s( tmpbuf, 128 );
    printf( "OS 日付:\t\t\t\t%s\n", tmpbuf );

    // Get UNIX-style time and display as number and string. 
    //time_t型変数のアドレスを引数とするtime関数でltimeに現在日時秒数を協定世界時UTCで設定。
	time( &ltime );
    printf( "秒数による日時情報 1970年1月1日基準:\t%ld\n", ltime );
    
	//ctime_sは第三引数のtime_t型UTC秒数情報から第一引数の文字列ポインタへ現在のシステムのタイムゾーン時刻生成する関数。
	err = ctime_s(timebuf, 26, &ltime);
    if (err)
    {
       printf("ctime_s failed due to an invalid argument.");
       exit(1);
    }
    printf( "現在日時情報 曜日月(英語):\t\t%s", timebuf );


    //_gmtime64_s関数は第一引数のtm型構造体に第二引数で取得したUTC秒数情報からグリニッジ平均時Greenwich Mean Timeすなわち協定世界時を生成する関数。
	//※GMTが天体観測によって管理される時間で1秒の長さが微変動するもの、UTCは原子時計でGMTの情報をもとにうるう秒を入れたりして調整が入る。
    err = _gmtime64_s( &gmt, &ltime );
    if (err)
    {
       printf("_gmtime64_s failed due to an invalid argument.");
    }
	//asctime_sは第三引数のtm型構造体に格納された情報から第一引数の文字列ポインタへ生成する関数。
    err = asctime_s(timebuf, 26, &gmt);
    if (err)
    {
       printf("asctime_s failed due to an invalid argument.");
       exit(1);
    }
    printf( "協定世界時 UTC:\t\t\t\t%s", timebuf );

    //_localtime64_sはtime_t型UTC秒数情報から第一引数のtm型構造体に現在のタイムゾーンの時刻を設定する関数。 
    err = _localtime64_s( &today, &ltime );
    if (err)
    {
       printf("_localtime64_s failed due to an invalid argument.");
       exit(1);
    }
	//tm構造体のtoday変数の24時間形式を格納するメンバ変数tm_hourの値が12以上なら午後なので、午前午後情報格納文字列の内容をPMに。
    if( today.tm_hour >= 12 )
    {
        strcpy_s( ampm, sizeof(ampm), "PM" );
        today.tm_hour -= 12;//午後で表現するので、12を引いた。但し、この後tm構造体変数として違う関数への引数とかにすると時間が12時間前になるので用途に注意。
    }

    err = asctime_s(timebuf, 26, &today);
    if (err)
    {
       printf("asctime_s failed due to an invalid argument.");
       exit(1);
    }

	//timebufの12文字目から8文字が時間なので、その部分だけを出力し、ampmの情報を付加する。
    printf( "12時間表記 時間:\t\t\t%.8s %s\n", timebuf + 11, ampm);


    //_timeb64構造体に取得したタイムゾーン現在時刻のメンバ変数出力
    printf( "現在時刻のミリ秒:\t\t\t%03u[ミリ秒]\n", structtimebTime.millitm );
    printf( "協定世界時とタイムゾーン時刻の差:\t%d\n", structtimebTime.timezone / 60 );

	size_t* pReturnValue = new size_t;
	int index = structtimebTime.dstflag;

	//タイムゾーン名の文字列長をpReturnValueに取得
	_get_tzname(pReturnValue, NULL, 0, index);

	char* timeZoneName = new char[*pReturnValue + 1];
	size_t sizeInBytes = *pReturnValue + 1;

	//タイムゾーン名の取得
	_get_tzname(pReturnValue, timeZoneName, sizeInBytes, index);

	printf( "タイムゾーン名:\t\t\t\t%s\n", timeZoneName ); 
    printf( "タイムゾーン名文字数:\t\t\t%d[byte]\n", *pReturnValue ); 
    printf( "夏時間適用フラグ:\t\t\t%s\n", structtimebTime.dstflag ? "夏時間" : "夏時間非適用" );

    //mktime関数は初期値が与えられているtm型構造体の情報から日時情報を生成し、他のメンバ変数の設定を行う関数
    if(mktime(&xmas) != (time_t)-1)
    {
       err = asctime_s(timebuf, 26, &xmas);
       if (err)
       {
          printf("asctime_s failed due to an invalid argument.");
          exit(1);
       }
       printf( "指定日付\t\t\t\t%s\n", timebuf );
    }

    err = _localtime64_s( &today, &ltime );
    if (err)
    {
        printf(" _localtime64_s failed due to invalid arguments.");
        exit(1);
    }

    //★時間変数を文字列に変換する関数 strftime  
    *pReturnValue = strftime( tmpbuf, 128, "Today is %A, day %d of %B in the year %Y.\n", &today );
	printf("%d[byte]\n",* pReturnValue);
	//英語表記の曜日、月名が取得できる。
	strftime( tmpbuf, 128, "Today is %A, day %d of %B in the year %Y.\n", &today );
    printf( tmpbuf );

	*pReturnValue = _strftime_l(tmpbuf, 128, "今日は(%y年)%Y年%B(%b月)%d日(%A:%a;%w番目の曜日 %W回目の%A) %U週目 %j日目 現在時刻は(%p %I)%H:%M:%S\n", &today, localeJpanease );
	printf("%d[byte]\n",* pReturnValue);
	//ロケール設定により日本語表記の曜日、月名が取得できる。
	_strftime_l(tmpbuf, 128, "今日は(%y年)%Y年%B(%b月)%d日(%A:%a;%w番目の曜日 %W回目の%A) %U週目 %j日目 現在時刻は(%p %I)%H:%M:%S\n", &today, localeJpanease );
    printf( tmpbuf );

	*pReturnValue = _strftime_l(tmpbuf, 128, "%#c,%#x,%%,%#S,%#a,%#%,%z,%Z\n", &today, localeJpanease );
	printf("%d[byte]\n",* pReturnValue);
	//ロケール設定により日本語表記の曜日、月名が取得できる。
	_strftime_l(tmpbuf, 128, "%#c,%#x,%%,%#S,%#a,%#%,%z,%Z\n", &today, localeJpanease );
    printf( tmpbuf );
  return 0;
}


出力結果

OS 時間:                                20:18:03
OS 日付:                                10/13/14
秒数による日時情報 1970年1月1日基準:    1413199083
現在日時情報 曜日月(英語):              Mon Oct 13 20:18:03 2014
協定世界時 UTC:                         Mon Oct 13 11:18:03 2014
12時間表記 時間:                        08:18:03 PM
現在時刻のミリ秒:                       006[ミリ秒]
協定世界時とタイムゾーン時刻の差:       -9
タイムゾーン名:                         東京 (標準時)
タイムゾーン名文字数:                   14[byte]
夏時間適用フラグ:                       夏時間非適用
指定日付                                Sat Dec 25 12:00:00 1993

53[byte]
Today is Monday, day 13 of October in the year 2014.
80[byte]
今日は(14年)2014年10月(10月)13日(月曜日:月;1番目の曜日 41回目の月曜日) 41週目 286日目 現在時刻は(午後 08)20:18:03
59[byte]
2014年10月13日 20:18:03,2014年10月13日,,3,月,,東京 (標準時),東京 (標準時)

と上記のように時間の表記を文字列に変換できます。ICUによる変換は時間があれば、サンプルを作成したいと思います。


それでICUによる時間の文字列化ですが、 サンプルは以下にしめすような感じです。UCalendarというクラスとCalendar+SimpleFormatクラスの2通りがありまして、UCalendarの方はちょっと使いにくそうでした。あとは、タイムゾーンの対応がものすごくて、びっくりします。言語別にUnicodeで表記が出来ます。でも言語別を指定するためのキーワードを知るのが大変で、カレンダーのロケールについては、詳細な一覧方法は提供されていないようにも思います。オーソドックスなロケールは一覧できますが、これでは不十分でして、一番詳しいのは、英語のページになりますが、http://www.unicode.org/reports/tr35/ のサイトになります。長すぎる。日本語のロケールについては、"ja","ja-JP"の基本的な二つに加えてカレンダー用のロケールとして"ja_JP_TRADITIONAL","ja_JP@calendar=japanese"があり、こちらのロケールを使うと公年号(645年の大化から始まって明治 大正 昭和 平成)を使った表現に対応できます。他にも"ja"と同じですが、"japanese"や"jpn"でも良さそうでした。jpnはISO3表記ってことになるのでしょうか?難しいです。これが世界684の地域と620のタイムゾーンにわたって定義されているのでしょうから、なんつうか、やっぱ戦争でもして世界征服して一つの国になったほうがよかったんじゃないのかって思うくらい言語は分かれています。いや冗談冗談。戦争は駄目ですよ。不謹慎な冗談はやめた方がいいか…何の得にもならんしね。こうやって、全く面白くもないのに、おもしろいとおもって書いたことをつつかれて、社会的に抹殺されていくんだろうなぁ。有名人は大変だねぇ。あり得ない発言はだめなんですもんね。わけわからんことを言うと影響力ありますからね。そりゃ世間から指摘されますよね。全く思ってもないようなことを言うのだめだわな。


まずは、UCalendarを使ったサンプル。

#pragma once
#include "stdafx.h"
#include <stdlib.h>
#include <string>      //std::string型定義
#include <mbstring.h>	 //mbs***関数
#include <iostream>    //cpp cout etc 一般入出力関数
#include <locale>      //Locale関数
#include <tchar.h>     //TCHAR型+_tcs***関数
#include "atlstr.h"    //CString,CStringA,CStringWおよびLPTSTR,LPWSTR,LPSTR,LPCTSTR,LPCWSTR,LPCSTRの定義プリプロセッサ
#include "atlbase.h"   //同上
#include "cstringt.h"  //CStringTの定義プリプロセッサ
#include <vector>      //Vector型テンプレートクラス

#include <sys/timeb.h> //Timeb型構造体

//_bstr_t型を利用するプリプロセッサ(ライブラリはデバッグモードとリリースモード個別設定)
#include "comutil.h"

#ifdef _DEBUG
#pragma comment(lib, "comsuppwd.lib")
#else
#pragma comment(lib, "comsuppw.lib")
#endif
//_bstr_t _End

//ICU ucnvプリプロセッサ(ライブラリはデバッグモードとリリースモード個別設定)
#include <unicode/ucnv.h>     //ucnv文字コード変換ライブラリヘッダ
#include <unicode/translit.h> //文字変換ライブラリヘッダ
#include <unicode/regex.h>    //正規表現
#include <unicode/ucsdet.h>   //文字コード判定
#include <unicode/ucal.h>   //文字コード判定
#include <unicode/uclean.h> //u_cleanup関数
/* for uloc_getDefault() */
#include <unicode/uloc.h>
#include <unicode/calendar.h>
#include <unicode/smpdtfmt.h>

//#include "unicode/utypes.h"
//#include "unicode/locid.h"
//#include "unicode/unistr.h"
//#include "unicode/tzfmt.h"
//#include "unicode/tznames.h"


#ifdef _DEBUG
#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuucd.lib")  //ICU ucnvを使うために必要なライブラリ
#pragma comment(lib, "icuind.lib")  //ICU Transliterate関数を使うために必要なライブラリ 正規表現関数も
#pragma comment(lib, "iculed.lib")
#pragma comment(lib, "iculxd.lib")  
#pragma comment(lib, "icuiod.lib")  
#pragma comment(lib, "icutud.lib")  

#else
//#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuind.lib")  //ICU Transliterate関数を使うために必要なライブラリ 正規表現関数も
#pragma comment(lib, "icuuc.lib")   //ICU ucnvを使うために必要なライブラリ
//#pragma comment(lib, "icuio.lib")
#endif
//Data   Library                     icudtXX(d).dll icudt(d).lib
//Common Library                     icuucXX(d).dll icuuc(d).lib
//Internationalization(i18n) Library icuinXX(d).dll icuin(d).lib
//Layout Engine                      iculeXX(d).dll icule(d).lib
//Layout Extention Engine            iculxXX(d).dll iculx(d).lib
//ICU I/O(Unicode stdio) Library     icuioXX(d).dll icuio(d).lib
//Tool Utility Library               icutuXX(d).dll icutu(d).lib

//ICU ucnv _End

#include "UnicodeConverter.h"

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
  _tsetlocale(LC_ALL, _T("Japanese")); //ロケールセットすると_l関数でロケール指定しなくても日本語が使われます。

  _locale_t localeJpanease;
  localeJpanease = _create_locale(LC_ALL, "Japanese");

  int32_t int32TimeZoneID;
  UChar* pucharResult = new UChar[1024];

  UErrorCode err = U_ZERO_ERROR;
  printf("%s\n",uloc_getDefault());
  UCalendar* cal = ucal_open(
          L"Asia/Tokyo", 10, //TimezoneID と文字数
          uloc_getDefault(), //Locale名 "ja_JP"
          UCAL_TRADITIONAL,  //カレンダータイプ
          &err);
  printf("errorNum?->%d\n",err);
  if (!cal)
  {
    err = U_ZERO_ERROR;
    fprintf(stderr, "ICU error: %s\n", u_errorName(err));
    u_cleanup();
    return ;
  }
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  //★ここからしばらくは日付表示とは関係の無い、余計なプログラム

  //属性値取得関数 最初の曜日の番号は1 
  printf("★現在のオープンしているZoneIDの各種属性値を取得する関数。\n");
  printf("Attrbute->%d\n", ucal_getAttribute(cal, UCAL_FIRST_DAY_OF_WEEK));
  printf("\n");


  //ゾーンIDを取得する関数。もちろんAsia/Tokyo。
  printf("★現在のオープンしているZoneIDのTimeZoneIDをUchar型変数に取得し、その文字数を返す関数\n");
  err = U_ZERO_ERROR;
  int32TimeZoneID = ucal_getTimeZoneID(cal, pucharResult, 1024, &err);
  wprintf(L"TimezoneID->%d[Byte]:%s\n",int32TimeZoneID,pucharResult);//10:Asia/Tokyo
  printf("\n");


  //タイプによるロケール名を取得。
  //ULOC_ACTUAL_LOCALE->ja
  //ULOC_VALID_LOCALE->ja_JP
  //ULOC_REQUESTED_LOCALE->NULL
  printf("★現在のオープンしているZoneIDのULocDataLocaleTypeによる名前を取得。\n");
  err = U_ZERO_ERROR;
  printf("TypeLocale->%s\n", ucal_getLocaleByType(cal, ULOC_VALID_LOCALE, &err));
  printf("\n");
  
  //★ここまでが、かなり余計なプログラムでした。
  //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
 
  //日付をセットする。
  err = U_ZERO_ERROR;
  ucal_setDateTime(cal, 2014, UCAL_OCTOBER, 17, 22, 45, 00, &err);//直接指定。
  ucal_setMillis(cal, ucal_getNow(), &err);//現在日時での指定で上書き。ミリ秒単位で表現されるダブル型の数値。
  //関数名がミリ秒だけを扱うみたいに見えるけど、これで日付時刻ミリ秒まで全部セットされる。
  


  //特定のフィールドの制限値取得 月の最大。11でした。年の最大は144683です。14万年…
  printf("★現在のオープンしているZoneIDの各種フィールド制限値の取得関数\n");
  int32_t int32TempLimitValue;
  err = U_ZERO_ERROR;
  int32TempLimitValue = ucal_getLimit(cal,UCAL_MILLISECOND,UCAL_MINIMUM, &err);
  printf("Limit->%d\n",int32TempLimitValue);
  printf("\n");

  //特定のフィールドのロール 時間を追加したりできる。3週間追加する例。コメント化してますけど。
  printf("★現在のオープンしているZoneIDの各種フィールドロール関数\n");
  int32_t int32Amount = 3;
  err = U_ZERO_ERROR;
  //ucal_roll(cal, UCAL_WEEK_OF_YEAR, int32Amount,&err);
  printf("Limit->%d\n",int32Amount);
  printf("\n");

  err = U_ZERO_ERROR;
  UDate udateGetMillis;
  err = U_ZERO_ERROR;
  udateGetMillis = ucal_getMillis(cal, &err);
  printf("★現在のオープンしているZoneIDのミリ秒を取得。\n");
  err = U_ZERO_ERROR;
  printf("GetMilli->%f %f %f\n", udateGetMillis , ucal_getNow(), ucal_getGregorianChange(cal, &err));
  printf("\n");
  
  //日時の出力
  printf("Date->%d/%d/%d\n",
          ucal_get(cal, UCAL_YEAR, &err),
          ucal_get(cal, UCAL_MONTH, &err) + 1,
          ucal_get(cal, UCAL_DATE, &err));
 
  printf("Time->%d:%d:%d:%d\n", 
          ucal_get(cal, UCAL_HOUR_OF_DAY, &err),
          ucal_get(cal, UCAL_MINUTE, &err) + 1,
          ucal_get(cal, UCAL_SECOND, &err),
          ucal_get(cal, UCAL_MILLISECOND, &err));
 
  printf("Othr->%d:%d:%d:%d\n",
          ucal_get(cal, UCAL_DAY_OF_WEEK, &err),
          ucal_get(cal, UCAL_AM_PM, &err) ,
          ucal_get(cal, UCAL_ZONE_OFFSET, &err),
          ucal_get(cal, UCAL_DST_OFFSET, &err)); 

  ucal_close(cal);
  u_cleanup();
  return 0;
}

出力 大体こんな感じ

★現在のオープンしているZoneIDの各種属性値を取得する関数。
Attrbute->1

★現在のオープンしているZoneIDのTimeZoneIDをUchar型変数に取得し、その文字数を返す関数
TimezoneID->10[Byte]:Asia/Tokyo

★現在のオープンしているZoneIDのULocDataLocaleTypeによる名前を取得。
TypeLocale->ja_JP

★現在のオープンしているZoneIDの各種フィールド制限値の取得関数
Limit->0

★現在のオープンしているZoneIDの各種フィールドロール関数
Limit->3

★現在のオープンしているZoneIDのミリ秒を取得。
GetMilli->1413654266519.000000 1413654266529.000000 -12219292800000.000000

Date->2014/10/19
Time->2:45:26:519
Othr->1:0:32400000:0

そして、Calendar+SimpleFormatクラスを使うサンプルは以下のような感じ。上記のプログラムの続きなので、インクルードとか飛ばします。

  UErrorCode status = U_ZERO_ERROR;
  //Calendar *cals = Calendar::createInstance(status);//日本語カレンダーロケールは ja_JP@calendar=japanese ja_JP_TRADITIONAL ja_JP ja japaneseが指定可能
  Calendar *cals = Calendar::createInstance("ja_JP@calendar=japanese", status);
  SimpleDateFormat *fmt = new SimpleDateFormat(UnicodeString("SSS S U Q q W w e F D zzzz zzz zz z EEE EE E YYYY YYY YY Y yyyy yyy yy v GG G y.M.d vv HH:mm:ss vvvv K:mm a vvv hh:mm h:m KK:mm  H:m aaa yyyyy.MM.dd GGG"), status);
  //SimpleDateFormat ★ja_JP(引数無し) ☆ja_JP_TRADITIONAL 1988年基準 年引数が1なら1989年で公元号1年 公元号名は表示されない。
  //z    タイムゾーン名(★GMT+09:18:59 ☆JST)
  //zz   タイムゾーン  (★GMT+09:18:59 ☆JST)
  //zzz  タイムゾーン  (★GMT+09:18:59 ☆JST)
  //zzzz タイムゾーン年-タイムゾーン名(★2014-GMT+09:18:59 ☆2014-日本標準時)
  //v   タイムゾーン年-タイムゾーン名(★2014-日本時間 ☆2014-JST)
  //vv  タイムゾーン  (★表記無し)
  //vvv タイムゾーン  (★表記無し)
  //vvvv タイムゾーン名(★日本時間 ☆日本標準時)
  //G    グレゴリオ暦年-歴名(★2014-西暦 ☆平成)
  //GG   グレゴリオ暦名(年数値無し★西暦 ☆平成)
  //GGG  グレゴリオ暦名(年数値無し★西暦 ☆平成)
  //YYYY グレゴリオ暦年-公元号年(★2014-0026 ☆2014-2014)
  //YYY  公元号年orグレゴリオ暦年(3桁表現最小表現 0埋め★026 ☆2014)
  //YY   公元号年orグレゴリオ暦年(2桁表現固定0埋め★26 ☆14)
  //Y    公元号年orグレゴリオ暦年(最小文字列★26 ☆2014)
  //yyyyy公元号年5桁数値のみ(0埋め)
  //yyyy 公元号年4桁数値のみ(0埋め)
  //yyy  公元号年3桁数値のみ(0埋め)
  //yy   公元号年2桁数値のみ(0埋め)
  //y    公元号年数値のみ
  //M    数値表現の月(最小文字列)※小文字だと時間の分
  //MM   数値表現の月(2桁表現)※小文字だと時間の分
  //MMMMM数値表現の月(最小文字列)※小文字だと時間の分
  //d    数値表現の日(最小文字列)
  //dd   数値表現の日(2桁表現)
  //EEE  曜日(★土 1日前の曜日 ☆日)
  //EE   曜日(★土 1日前の曜日 ☆日)
  //E    曜日(★土 1日前の曜日 ☆日)
  //a  午前・午後表記(漢字日本語★午前)
  //K    数値表現の時間(0時は0のまま 最小文字列)
  //KK   数値表現の時間(0時は0のまま 2桁表現0埋め)
  //H    数値表現の時間(24時間制 0時は0のまま 最小文字列)
  //HH   数値表現の時間(24時間制 0時は0のまま 2桁表現0埋め)
  //h    数値表現の時間(0時は12時 最小文字列)
  //hh   数値表現の時間(0時は12時 2桁表現0埋め)
  //m    数値用言の分(最小文字列)※大文字だと日付の月
  //mm   数値用言の分(2桁表現0埋め)※大文字だと日付の月
  //s    数値表現の秒(最小文字列)
  //ss   数値表現の秒(2桁表現0埋め)
  //その他
  //S    ミリ秒
  //u    extended年
  //Q    四半期 テキスト&番号
  //q
  //w    週目 通年
  //W    週目 月
  //D    日目 通年    
  //e    曜日番号
  //F    何回目の曜日 月  
  //

  fmt->adoptCalendar(cals);//フォーマットにカレンダーを設定。
  for(int k=2014;k<2015;k++){
    cals->set(k - 1988 , 10 - 1, 17, 14, 0, 0);//カレンダーの日付を数値で設定する方法 ja_JP_TRADITIONAは1988年基準 昭和元年が1989年
    UDate now = cals->getTime(status);//nowに現在時間を取得。UDateはDouble型と同じです。
    UnicodeString buf;
    fmt->format(now, buf);//UnicodeString型のbufの内容をnowに設定されている時間を文字列にしたものに変換。変換は指定されているフォーマットの形式。
    char* dst = new char[256];
    std::string conv(buf.extract(0, buf.length(), 0, "shift_jis"), '\0');//string型convをbufの文字列のShiftJIS形式長さ分確保 初期値は\0で埋める
    //UnicodeStringのextractはbufの中身を(第一引数 =0)文字目から、(第二引数 =buf.length())文字目までを第四引数にしていされた文字コードの型に変換して第三引数の変数に格納する関数
    //変換先が指定されていない場合でも変換後の文字サイズが返却されます。ここでは文字サイズを取得してconvを初期化しています。
    buf.extract(0, buf.length(), &conv[0], "shift_jis");//ここでconvに実際に変換後の文字を格納。
    memcpy(dst, conv.c_str(), conv.size());//convの内容をchar型dstにコピー
    printf("%d-%s ", k, conv.c_str());//convの内容を出力。
    delete dst;
  }
  delete fmt;

出力 いろんな種類の出力をがっつり記述したので、わけわからないことになってますが

2014-274 2 26 4 4 3 42 6 3 290 日本標準時 JST JST JST 金 金 金 2014 2014 14 2014 0026 026 26 JST 平成 平成 26.10.17  14:00:00 日本標準時 2:00 午後  02:00 2:0 02:00  14:0 午後 00026.10.17 平成

あとのほうのプログラムの方がタイムゾーン名を各国語で表示できる分、国際化には向いているかもしれませんが、そんなプログラム組む人いるのかなぁ。 ロケール名一覧は後で示す日本語タイムゾーンの各ロケールごとの表示一覧に、タイムゾーン名一覧は各タイムゾーンの日本語表記一覧に記載しました。


UCalendarクラスやCalendarクラスの使い方はICUのサイトを確認して下さい。なんやかんやで長い記事になっているせいで忘れられているかもしれませんが、ここでは文字列操作に関する情報を中心に扱っています。ちなみにICUのサイトを見てもクラス仕様書がきっちりのっているものの個別の関数の具体的な使い方についてはあまり触れられていません。関数名と機能から、あぁこういう風に使って欲しいんだなっていうのを感じ取るスキルが求められます。なので自分もカレンダーのロケール名指定方法の詳細な一覧は入手できませんでした。もしわかった人がいたら、各自のブログやWikiで情報を発信して下さい。よろしくお願いします。たぶんICUをここまで掘り下げて使っている人はいないはず。上記に記述した各タイムゾーンやロケールの一覧を取得するにはStringEnumerationやUEnumerationクラスの使い方を理解したり、Locale、Timezoneクラスの構造を理解することが求められると思います。頑張ってください。


それから、年号なんですけどサンプルプログラムのFor文では2014年だけの一回のループになってますが、ちょこっと変えて645年からにすると、まぁ素敵なほどに年号を羅列してくれます。他にも、日本のタイムゾーンが各国でどのように表示されているのか、あるいは、タイムゾーンがどれほどあるのかを一覧した、日本目線の一覧を貼っておきます。ICUは文字列操作の処理範囲をここまで広くかんがえているんだなあと考えさせられます。一種のデータベースと言えるかもしれません。うすっぺらな自分では調べきることは困難な範囲です。日本のことですらあまりわかっていないし…


プログラムでUNICODE文字を出力してますが、コンソール表示のときにはSJISに変換されるため、SJISで表現できない文字は文字化け?というか変換先がないため正しく表示されません。正しく表示させたい場合は先の項目で説明したとおり、ファイル入出力をつかってUNICODE形式でテキストファイルに出力するといいです。Unicode対応のメモ帳で閲覧できます。Terapadは無理。自分は今回の機会にサクラエディタを導入しました。Unicodeも対応している優秀なエディタであることに気付かされました。UNICODEフォントですが、Windows標準のフォントファイルは実はUnicodeフォントになっています。すごいです。

年号サンプル

645-大化1:646-大化2:647-大化3:648-大化4:649-大化5:650-白雉1:651-白雉2:652-白雉3:653-白雉4:654-白雉5:655-白雉6:656-白雉7:657-白雉8:658-白雉9:659-白雉10:660-白雉11:661-白雉12:662-白雉13:663-白雉14:664-白雉15:665-白雉16:666-白雉17:667-白雉18:668-白雉19:669-白雉20:670-白雉21:671-白雉22:672-白鳳1:673-白鳳2:674-白鳳3:675-白鳳4:676-白鳳5:677-白鳳6:678-白鳳7:679-白鳳8:680-白鳳9:681-白鳳10:682-白鳳11:683-白鳳12:684-白鳳13:685-白鳳14:686-朱鳥1:687-朱鳥2:688-朱鳥3:689-朱鳥4:690-朱鳥5:691-朱鳥6:692-朱鳥7:693-朱鳥8:694-朱鳥9:695-朱鳥10:696-朱鳥11:697-朱鳥12:698-朱鳥13:699-朱鳥14:700-朱鳥15:701-大宝1:702-大宝2:703-大宝3:704-慶雲1:705-慶雲2:706-慶雲3:707-慶雲4:708-和銅1:709-和銅2:710-和銅3:711-和銅4:712-和銅5:713-和銅6:714-和銅7:715-霊亀1:716-霊亀2:717-霊亀3:718-養老2:719-養老3:720-養老4:721-養老5:722-養老6:723-養老7:724-神亀1:725-神亀2:726-神亀3:727-神亀4:728-神亀5:729-天平1:730-天平2:731-天平3:732-天平4:733-天平5:734-天平6:735-天平7:736-天平8:737-天平9:738-天平10:739-天平11:740-天平12:741-天平13:742-天平14:743-天平15:744-天平16:745-天平17:746-天平18:747-天平19:748-天平20:749-天平勝宝1:750-天平勝宝2:751-天平勝宝3:752-天平勝宝4:753-天平勝宝5:754-天平勝宝6:755-天平勝宝7:756-天平勝宝8:757-天平宝字1:758-天平宝字2:759-天平宝字3:760-天平宝字4:761-天平宝字5:762-天平宝字6:763-天平宝字7:764-天平宝字8:765-天平神護1:766-天平神護2:767-神護景雲1:768-神護景雲2:769-神護景雲3:770-宝亀1:771-宝亀2:772-宝亀3:773-宝亀4:774-宝亀5:775-宝亀6:776-宝亀7:777-宝亀8:778-宝亀9:779-宝亀10:780-宝亀11:781-天応1:782-延暦1:783-延暦2:784-延暦3:785-延暦4:786-延暦5:787-延暦6:788-延暦7:789-延暦8:790-延暦9:791-延暦10:792-延暦11:793-延暦12:794-延暦13:795-延暦14:796-延暦15:797-延暦16:798-延暦17:799-延暦18:800-延暦19:801-延暦20:802-延暦21:803-延暦22:804-延暦23:805-延暦24:806-大同1:807-大同2:808-大同3:809-大同4:810-弘仁1:811-弘仁2:812-弘仁3:813-弘仁4:814-弘仁5:815-弘仁6:816-弘仁7:817-弘仁8:818-弘仁9:819-弘仁10:820-弘仁11:821-弘仁12:822-弘仁13:823-弘仁14:824-天長1:825-天長2:826-天長3:827-天長4:828-天長5:829-天長6:830-天長7:831-天長8:832-天長9:833-天長10:834-承和1:835-承和2:836-承和3:837-承和4:838-承和5:839-承和6:840-承和7:841-承和8:842-承和9:843-承和10:844-承和11:845-承和12:846-承和13:847-承和14:848-嘉祥1:849-嘉祥2:850-嘉祥3:851-仁寿1:852-仁寿2:853-仁寿3:854-仁寿4:855-斉衡2:856-斉衡3:857-天安1:858-天安2:859-貞観1:860-貞観2:861-貞観3:862-貞観4:863-貞観5:864-貞観6:865-貞観7:866-貞観8:867-貞観9:868-貞観10:869-貞観11:870-貞観12:871-貞観13:872-貞観14:873-貞観15:874-貞観16:875-貞観17:876-貞観18:877-元慶1:878-元慶2:879-元慶3:880-元慶4:881-元慶5:882-元慶6:883-元慶7:884-元慶8:885-仁和1:886-仁和2:887-仁和3:888-仁和4:889-寛平1:890-寛平2:891-寛平3:892-寛平4:893-寛平5:894-寛平6:895-寛平7:896-寛平8:897-寛平9:898-昌泰1:899-昌泰2:900-昌泰3:901-延喜1:902-延喜2:903-延喜3:904-延喜4:905-延喜5:906-延喜6:907-延喜7:908-延喜8:909-延喜9:910-延喜10:911-延喜11:912-延喜12:913-延喜13:914-延喜14:915-延喜15:916-延喜16:917-延喜17:918-延喜18:919-延喜19:920-延喜20:921-延喜21:922-延喜22:923-延長1:924-延長2:925-延長3:926-延長4:927-延長5:928-延長6:929-延長7:930-延長8:931-承平1:932-承平2:933-承平3:934-承平4:935-承平5:936-承平6:937-承平7:938-天慶1:939-天慶2:940-天慶3:941-天慶4:942-天慶5:943-天慶6:944-天慶7:945-天慶8:946-天慶9:947-天暦1:948-天暦2:949-天暦3:950-天暦4:951-天暦5:952-天暦6:953-天暦7:954-天暦8:955-天暦9:956-天暦10:957-天暦11:958-天徳2:959-天徳3:960-天徳4:961-応和1:962-応和2:963-応和3:964-康保1:965-康保2:966-康保3:967-康保4:968-安和1:969-安和2:970-天禄1:971-天禄2:972-天禄3:973-天禄4:974-天延2:975-天延3:976-貞元1:977-貞元2:978-貞元3:979-天元2:980-天元3:981-天元4:982-天元5:983-永観1:984-永観2:985-寛和1:986-寛和2:987-永延1:988-永延2:989-永祚1:990-永祚2:991-正暦2:992-正暦3:993-正暦4:994-正暦5:995-長徳1:996-長徳2:997-長徳3:998-長徳4:999-長保1:1000-長保2:1001-長保3:1002-長保4:1003-長保5:1004-寛弘1:1005-寛弘2:1006-寛弘3:1007-寛弘4:1008-寛弘5:1009-寛弘6:1010-寛弘7:1011-寛弘8:1012-寛弘9:1013-長和2:1014-長和3:1015-長和4:1016-長和5:1017-寛仁1:1018-寛仁2:1019-寛仁3:1020-寛仁4:1021-治安1:1022-治安2:1023-治安3:1024-万寿1:1025-万寿2:1026-万寿3:1027-万寿4:1028-長元1:1029-長元2:1030-長元3:1031-長元4:1032-長元5:1033-長元6:1034-長元7:1035-長元8:1036-長元9:1037-長暦1:1038-長暦2:1039-長暦3:1040-長暦4:1041-長久2:1042-長久3:1043-長久4:1044-長久5:1045-寛徳2:1046-永承1:1047-永承2:1048-永承3:1049-永承4:1050-永承5:1051-永承6:1052-永承7:1053-天喜1:1054-天喜2:1055-天喜3:1056-天喜4:1057-天喜5:1058-康平1:1059-康平2:1060-康平3:1061-康平4:1062-康平5:1063-康平6:1064-康平7:1065-治暦1:1066-治暦2:1067-治暦3:1068-治暦4:1069-延久1:1070-延久2:1071-延久3:1072-延久4:1073-延久5:1074-承保1:1075-承保2:1076-承保3:1077-承保4:1078-承暦2:1079-承暦3:1080-承暦4:1081-永保1:1082-永保2:1083-永保3:1084-応徳1:1085-応徳2:1086-応徳3:1087-寛治1:1088-寛治2:1089-寛治3:1090-寛治4:1091-寛治5:1092-寛治6:1093-寛治7:1094-寛治8:1095-嘉保2:1096-嘉保3:1097-永長2:1098-承徳2:1099-康和1:1100-康和2:1101-康和3:1102-康和4:1103-康和5:1104-長治1:1105-長治2:1106-嘉承1:1107-嘉承2:1108-天仁1:1109-天仁2:1110-天永1:1111-天永2:1112-天永3:1113-永久1:1114-永久2:1115-永久3:1116-永久4:1117-永久5:1118-元永1:1119-元永2:1120-保安1:1121-保安2:1122-保安3:1123-保安4:1124-天治1:1125-天治2:1126-大治1:1127-大治2:1128-大治3:1129-大治4:1130-大治5:1131-天承1:1132-長承1:1133-長承2:1134-長承3:1135-保延1:1136-保延2:1137-保延3:1138-保延4:1139-保延5:1140-保延6:1141-永治1:1142-康治1:1143-康治2:1144-天養1:1145-久安1:1146-久安2:1147-久安3:1148-久安4:1149-久安5:1150-久安6:1151-仁平1:1152-仁平2:1153-仁平3:1154-仁平4:1155-久寿2:1156-保元1:1157-保元2:1158-保元3:1159-平治1:1160-永暦1:1161-応保1:1162-応保2:1163-長寛1:1164-長寛2:1165-永万1:1166-仁安1:1167-仁安2:1168-仁安3:1169-嘉応1:1170-嘉応2:1171-承安1:1172-承安2:1173-承安3:1174-承安4:1175-安元1:1176-安元2:1177-治承1:1178-治承2:1179-治承3:1180-治承4:1181-養和1:1182-寿永1:1183-寿永2:1184-元暦1:1185-文治1:1186-文治2:1187-文治3:1188-文治4:1189-文治5:1190-建久1:1191-建久2:1192-建久3:1193-建久4:1194-建久5:1195-建久6:1196-建久7:1197-建久8:1198-建久9:1199-正治1:1200-正治2:1201-建仁1:1202-建仁2:1203-建仁3:1204-元久1:1205-元久2:1206-建永1:1207-建永2:1208-承元2:1209-承元3:1210-承元4:1211-建暦1:1212-建暦2:1213-建暦3:1214-建保2:1215-建保3:1216-建保4:1217-建保5:1218-建保6:1219-承久1:1220-承久2:1221-承久3:1222-貞応1:1223-貞応2:1224-貞応3:1225-嘉禄1:1226-嘉禄2:1227-嘉禄3:1228-安貞2:1229-寛喜1:1230-寛喜2:1231-寛喜3:1232-貞永1:1233-天福1:1234-天福2:1235-嘉禎1:1236-嘉禎2:1237-嘉禎3:1238-嘉禎4:1239-延応1:1240-仁治1:1241-仁治2:1242-仁治3:1243-寛元1:1244-寛元2:1245-寛元3:1246-寛元4:1247-宝治1:1248-宝治2:1249-建長1:1250-建長2:1251-建長3:1252-建長4:1253-建長5:1254-建長6:1255-建長7:1256-康元1:1257-正嘉1:1258-正嘉2:1259-正元1:1260-文応1:1261-弘長1:1262-弘長2:1263-弘長3:1264-文永1:1265-文永2:1266-文永3:1267-文永4:1268-文永5:1269-文永6:1270-文永7:1271-文永8:1272-文永9:1273-文永10:1274-文永11:1275-建治1:1276-建治2:1277-建治3:1278-弘安1:1279-弘安2:1280-弘安3:1281-弘安4:1282-弘安5:1283-弘安6:1284-弘安7:1285-弘安8:1286-弘安9:1287-弘安10:1288-正応1:1289-正応2:1290-正応3:1291-正応4:1292-正応5:1293-永仁1:1294-永仁2:1295-永仁3:1296-永仁4:1297-永仁5:1298-永仁6:1299-正安1:1300-正安2:1301-正安3:1302-正安4:1303-嘉元1:1304-嘉元2:1305-嘉元3:1306-嘉元4:1307-徳治2:1308-延慶1:1309-延慶2:1310-延慶3:1311-応長1:1312-正和1:1313-正和2:1314-正和3:1315-正和4:1316-正和5:1317-文保1:1318-文保2:1319-元応1:1320-元応2:1321-元亨1:1322-元亨2:1323-元亨3:1324-元亨4:1325-正中2:1326-嘉暦1:1327-嘉暦2:1328-嘉暦3:1329-元徳1:1330-元徳2:1331-元弘1:1332-元弘2:1333-元弘3:1334-建武1:1335-建武2:1336-延元1:1337-延元2:1338-延元3:1339-延元4:1340-興国1:1341-興国2:1342-興国3:1343-興国4:1344-興国5:1345-興国6:1346-興国7:1347-正平2:1348-正平3:1349-正平4:1350-正平5:1351-正平6:1352-正平7:1353-正平8:1354-正平9:1355-正平10:1356-正平11:1357-正平12:1358-正平13:1359-正平14:1360-正平15:1361-正平16:1362-正平17:1363-正平18:1364-正平19:1365-正平20:1366-正平21:1367-正平22:1368-正平23:1369-正平24:1370-建徳1:1371-建徳2:1372-文中1:1373-文中2:1374-文中3:1375-天授1:1376-天授2:1377-天授3:1378-天授4:1379-康暦1:1380-康暦2:1381-弘和1:1382-弘和2:1383-弘和3:1384-至徳1:1385-至徳2:1386-至徳3:1387-嘉慶1:1388-嘉慶2:1389-康応1:1390-明徳1:1391-明徳2:1392-明徳3:1393-明徳4:1394-応永1:1395-応永2:1396-応永3:1397-応永4:1398-応永5:1399-応永6:1400-応永7:1401-応永8:1402-応永9:1403-応永10:1404-応永11:1405-応永12:1406-応永13:1407-応永14:1408-応永15:1409-応永16:1410-応永17:1411-応永18:1412-応永19:1413-応永20:1414-応永21:1415-応永22:1416-応永23:1417-応永24:1418-応永25:1419-応永26:1420-応永27:1421-応永28:1422-応永29:1423-応永30:1424-応永31:1425-応永32:1426-応永33:1427-応永34:1428-正長1:1429-永享1:1430-永享2:1431-永享3:1432-永享4:1433-永享5:1434-永享6:1435-永享7:1436-永享8:1437-永享9:1438-永享10:1439-永享11:1440-永享12:1441-嘉吉1:1442-嘉吉2:1443-嘉吉3:1444-文安1:1445-文安2:1446-文安3:1447-文安4:1448-文安5:1449-宝徳1:1450-宝徳2:1451-宝徳3:1452-享徳1:1453-享徳2:1454-享徳3:1455-康正1:1456-康正2:1457-長禄1:1458-長禄2:1459-長禄3:1460-長禄4:1461-寛正2:1462-寛正3:1463-寛正4:1464-寛正5:1465-寛正6:1466-文正1:1467-応仁1:1468-応仁2:1469-文明1:1470-文明2:1471-文明3:1472-文明4:1473-文明5:1474-文明6:1475-文明7:1476-文明8:1477-文明9:1478-文明10:1479-文明11:1480-文明12:1481-文明13:1482-文明14:1483-文明15:1484-文明16:1485-文明17:1486-文明18:1487-長享1:1488-長享2:1489-延徳1:1490-延徳2:1491-延徳3:1492-明応1:1493-明応2:1494-明応3:1495-明応4:1496-明応5:1497-明応6:1498-明応7:1499-明応8:1500-明応9:1501-文亀1:1502-文亀2:1503-文亀3:1504-永正1:1505-永正2:1506-永正3:1507-永正4:1508-永正5:1509-永正6:1510-永正7:1511-永正8:1512-永正9:1513-永正10:1514-永正11:1515-永正12:1516-永正13:1517-永正14:1518-永正15:1519-永正16:1520-永正17:1521-大永1:1522-大永2:1523-大永3:1524-大永4:1525-大永5:1526-大永6:1527-大永7:1528-享禄1:1529-享禄2:1530-享禄3:1531-享禄4:1532-天文1:1533-天文2:1534-天文3:1535-天文4:1536-天文5:1537-天文6:1538-天文7:1539-天文8:1540-天文9:1541-天文10:1542-天文11:1543-天文12:1544-天文13:1545-天文14:1546-天文15:1547-天文16:1548-天文17:1549-天文18:1550-天文19:1551-天文20:1552-天文21:1553-天文22:1554-天文23:1555-天文24:1556-弘治2:1557-弘治3:1558-永禄1:1559-永禄2:1560-永禄3:1561-永禄4:1562-永禄5:1563-永禄6:1564-永禄7:1565-永禄8:1566-永禄9:1567-永禄10:1568-永禄11:1569-永禄12:1570-元亀1:1571-元亀2:1572-元亀3:1573-天正1:1574-天正2:1575-天正3:1576-天正4:1577-天正5:1578-天正6:1579-天正7:1580-天正8:1581-天正9:1582-天正10:1583-天正11:1584-天正12:1585-天正13:1586-天正14:1587-天正15:1588-天正16:1589-天正17:1590-天正18:1591-天正19:1592-天正20:1593-文禄2:1594-文禄3:1595-文禄4:1596-文禄5:1597-慶長2:1598-慶長3:1599-慶長4:1600-慶長5:1601-慶長6:1602-慶長7:1603-慶長8:1604-慶長9:1605-慶長10:1606-慶長11:1607-慶長12:1608-慶長13:1609-慶長14:1610-慶長15:1611-慶長16:1612-慶長17:1613-慶長18:1614-慶長19:1615-元和1:1616-元和2:1617-元和3:1618-元和4:1619-元和5:1620-元和6:1621-元和7:1622-元和8:1623-元和9:1624-寛永1:1625-寛永2:1626-寛永3:1627-寛永4:1628-寛永5:1629-寛永6:1630-寛永7:1631-寛永8:1632-寛永9:1633-寛永10:1634-寛永11:1635-寛永12:1636-寛永13:1637-寛永14:1638-寛永15:1639-寛永16:1640-寛永17:1641-寛永18:1642-寛永19:1643-寛永20:1644-寛永21:1645-正保2:1646-正保3:1647-正保4:1648-慶安1:1649-慶安2:1650-慶安3:1651-慶安4:1652-承応1:1653-承応2:1654-承応3:1655-明暦1:1656-明暦2:1657-明暦3:1658-万治1:1659-万治2:1660-万治3:1661-寛文1:1662-寛文2:1663-寛文3:1664-寛文4:1665-寛文5:1666-寛文6:1667-寛文7:1668-寛文8:1669-寛文9:1670-寛文10:1671-寛文11:1672-寛文12:1673-延宝1:1674-延宝2:1675-延宝3:1676-延宝4:1677-延宝5:1678-延宝6:1679-延宝7:1680-延宝8:1681-天和1:1682-天和2:1683-天和3:1684-貞享1:1685-貞享2:1686-貞享3:1687-貞享4:1688-元禄1:1689-元禄2:1690-元禄3:1691-元禄4:1692-元禄5:1693-元禄6:1694-元禄7:1695-元禄8:1696-元禄9:1697-元禄10:1698-元禄11:1699-元禄12:1700-元禄13:1701-元禄14:1702-元禄15:1703-元禄16:1704-宝永1:1705-宝永2:1706-宝永3:1707-宝永4:1708-宝永5:1709-宝永6:1710-宝永7:1711-正徳1:1712-正徳2:1713-正徳3:1714-正徳4:1715-正徳5:1716-享保1:1717-享保2:1718-享保3:1719-享保4:1720-享保5:1721-享保6:1722-享保7:1723-享保8:1724-享保9:1725-享保10:1726-享保11:1727-享保12:1728-享保13:1729-享保14:1730-享保15:1731-享保16:1732-享保17:1733-享保18:1734-享保19:1735-享保20:1736-元文1:1737-元文2:1738-元文3:1739-元文4:1740-元文5:1741-寛保1:1742-寛保2:1743-寛保3:1744-延享1:1745-延享2:1746-延享3:1747-延享4:1748-寛延1:1749-寛延2:1750-寛延3:1751-寛延4:1752-宝暦2:1753-宝暦3:1754-宝暦4:1755-宝暦5:1756-宝暦6:1757-宝暦7:1758-宝暦8:1759-宝暦9:1760-宝暦10:1761-宝暦11:1762-宝暦12:1763-宝暦13:1764-明和1:1765-明和2:1766-明和3:1767-明和4:1768-明和5:1769-明和6:1770-明和7:1771-明和8:1772-明和9:1773-安永2:1774-安永3:1775-安永4:1776-安永5:1777-安永6:1778-安永7:1779-安永8:1780-安永9:1781-天明1:1782-天明2:1783-天明3:1784-天明4:1785-天明5:1786-天明6:1787-天明7:1788-天明8:1789-寛政1:1790-寛政2:1791-寛政3:1792-寛政4:1793-寛政5:1794-寛政6:1795-寛政7:1796-寛政8:1797-寛政9:1798-寛政10:1799-寛政11:1800-寛政12:1801-享和1:1802-享和2:1803-享和3:1804-文化1:1805-文化2:1806-文化3:1807-文化4:1808-文化5:1809-文化6:1810-文化7:1811-文化8:1812-文化9:1813-文化10:1814-文化11:1815-文化12:1816-文化13:1817-文化14:1818-文政1:1819-文政2:1820-文政3:1821-文政4:1822-文政5:1823-文政6:1824-文政7:1825-文政8:1826-文政9:1827-文政10:1828-文政11:1829-文政12:1830-文政13:1831-天保2:1832-天保3:1833-天保4:1834-天保5:1835-天保6:1836-天保7:1837-天保8:1838-天保9:1839-天保10:1840-天保11:1841-天保12:1842-天保13:1843-天保14:1844-天保15:1845-弘化2:1846-弘化3:1847-弘化4:1848-嘉永1:1849-嘉永2:1850-嘉永3:1851-嘉永4:1852-嘉永5:1853-嘉永6:1854-嘉永7:1855-安政2:1856-安政3:1857-安政4:1858-安政5:1859-安政6:1860-万延1:1861-文久1:1862-文久2:1863-文久3:1864-元治1:1865-慶応1:1866-慶応2:1867-慶応3:1868-明治1:1869-明治2:1870-明治3:1871-明治4:1872-明治5:1873-明治6:1874-明治7:1875-明治8:1876-明治9:1877-明治10:1878-明治11:1879-明治12:1880-明治13:1881-明治14:1882-明治15:1883-明治16:1884-明治17:1885-明治18:1886-明治19:1887-明治20:1888-明治21:1889-明治22:1890-明治23:1891-明治24:1892-明治25:1893-明治26:1894-明治27:1895-明治28:1896-明治29:1897-明治30:1898-明治31:1899-明治32:1900-明治33:1901-明治34:1902-明治35:1903-明治36:1904-明治37:1905-明治38:1906-明治39:1907-明治40:1908-明治41:1909-明治42:1910-明治43:1911-明治44:1912-大正1:1913-大正2:1914-大正3:1915-大正4:1916-大正5:1917-大正6:1918-大正7:1919-大正8:1920-大正9:1921-大正10:1922-大正11:1923-大正12:1924-大正13:1925-大正14:1926-大正15:1927-昭和2:1928-昭和3:1929-昭和4:1930-昭和5:1931-昭和6:1932-昭和7:1933-昭和8:1934-昭和9:1935-昭和10:1936-昭和11:1937-昭和12:1938-昭和13:1939-昭和14:1940-昭和15:1941-昭和16:1942-昭和17:1943-昭和18:1944-昭和19:1945-昭和20:1946-昭和21:1947-昭和22:1948-昭和23:1949-昭和24:1950-昭和25:1951-昭和26:1952-昭和27:1953-昭和28:1954-昭和29:1955-昭和30:1956-昭和31:1957-昭和32:1958-昭和33:1959-昭和34:1960-昭和35:1961-昭和36:1962-昭和37:1963-昭和38:1964-昭和39:1965-昭和40:1966-昭和41:1967-昭和42:1968-昭和43:1969-昭和44:1970-昭和45:1971-昭和46:1972-昭和47:1973-昭和48:1974-昭和49:1975-昭和50:1976-昭和51:1977-昭和52:1978-昭和53:1979-昭和54:1980-昭和55:1981-昭和56:1982-昭和57:1983-昭和58:1984-昭和59:1985-昭和60:1986-昭和61:1987-昭和62:1988-昭和63:1989-平成1:1990-平成2:1991-平成3:1992-平成4:1993-平成5:1994-平成6:1995-平成7:1996-平成8:1997-平成9:1998-平成10:1999-平成11:2000-平成12:2001-平成13:2002-平成14:2003-平成15:2004-平成16:2005-平成17:2006-平成18:2007-平成19:2008-平成20:2009-平成21:2010-平成22:2011-平成23:2012-平成24:2013-平成25:2014-平成26

タイムゾーン名

ACT                              オーストラリア中部標準時
AET                              オーストラリア東部標準時
AGT                              アルゼンチン標準時
ART                              東ヨーロッパ標準時
AST                              アラスカ標準時
Africa/Abidjan                   グリニッジ標準時
Africa/Accra                     グリニッジ標準時
Africa/Addis_Ababa               東アフリカ時間
Africa/Algiers                   中央ヨーロッパ標準時
Africa/Asmara                    東アフリカ時間
Africa/Asmera                    東アフリカ時間
Africa/Bamako                    グリニッジ標準時
Africa/Bangui                    西アフリカ標準時
Africa/Banjul                    グリニッジ標準時
Africa/Bissau                    グリニッジ標準時
Africa/Blantyre                  中央アフリカ時間
Africa/Brazzaville               西アフリカ標準時
Africa/Bujumbura                 中央アフリカ時間
Africa/Cairo                     東ヨーロッパ標準時
Africa/Casablanca                西ヨーロッパ標準時
Africa/Ceuta                     中央ヨーロッパ標準時
Africa/Conakry                   グリニッジ標準時
Africa/Dakar                     グリニッジ標準時
Africa/Dar_es_Salaam             東アフリカ時間
Africa/Djibouti                  東アフリカ時間
Africa/Douala                    西アフリカ標準時
Africa/El_Aaiun                  西ヨーロッパ標準時
Africa/Freetown                  グリニッジ標準時
Africa/Gaborone                  中央アフリカ時間
Africa/Harare                    中央アフリカ時間
Africa/Johannesburg              南アフリカ標準時
Africa/Juba                      東アフリカ時間
Africa/Kampala                   東アフリカ時間
Africa/Khartoum                  東アフリカ時間
Africa/Kigali                    中央アフリカ時間
Africa/Kinshasa                  西アフリカ標準時
Africa/Lagos                     西アフリカ標準時
Africa/Libreville                西アフリカ標準時
Africa/Lome                      グリニッジ標準時
Africa/Luanda                    西アフリカ標準時
Africa/Lubumbashi                中央アフリカ時間
Africa/Lusaka                    中央アフリカ時間
Africa/Malabo                    西アフリカ標準時
Africa/Maputo                    中央アフリカ時間
Africa/Maseru                    南アフリカ標準時
Africa/Mbabane                   南アフリカ標準時
Africa/Mogadishu                 東アフリカ時間
Africa/Monrovia                  グリニッジ標準時
Africa/Nairobi                   東アフリカ時間
Africa/Ndjamena                  西アフリカ標準時
Africa/Niamey                    西アフリカ標準時
Africa/Nouakchott                グリニッジ標準時
Africa/Ouagadougou               グリニッジ標準時
Africa/Porto-Novo                西アフリカ標準時
Africa/Sao_Tome                  グリニッジ標準時
Africa/Timbuktu                  グリニッジ標準時
Africa/Tripoli                   東ヨーロッパ標準時
Africa/Tunis                     中央ヨーロッパ標準時
Africa/Windhoek                  西アフリカ標準時
America/Adak                     ハワイ・アリューシャン標準時
America/Anchorage                アラスカ標準時
America/Anguilla                 大西洋標準時
America/Antigua                  大西洋標準時
America/Araguaina                ブラジリア標準時
America/Argentina/Buenos_Aires   アルゼンチン標準時
America/Argentina/Catamarca      アルゼンチン標準時
America/Argentina/ComodRivadavia アルゼンチン標準時
America/Argentina/Cordoba        アルゼンチン標準時
America/Argentina/Jujuy          アルゼンチン標準時
America/Argentina/La_Rioja       アルゼンチン標準時
America/Argentina/Mendoza        アルゼンチン標準時
America/Argentina/Rio_Gallegos   アルゼンチン標準時
America/Argentina/Salta          アルゼンチン標準時
America/Argentina/San_Juan       アルゼンチン標準時
America/Argentina/San_Luis       西部アルゼンチン標準時
America/Argentina/Tucuman        アルゼンチン標準時
America/Argentina/Ushuaia        アルゼンチン標準時
America/Aruba                    大西洋標準時
America/Asuncion                 パラグアイ標準時
America/Atikokan                 アメリカ東部標準時
America/Atka                     ハワイ・アリューシャン標準時
America/Bahia                    ブラジリア標準時
America/Bahia_Banderas           アメリカ中部標準時
America/Barbados                 大西洋標準時
America/Belem                    ブラジリア標準時
America/Belize                   アメリカ中部標準時
America/Blanc-Sablon             大西洋標準時
America/Boa_Vista                アマゾン標準時
America/Bogota                   コロンビア標準時
America/Boise                    アメリカ山地標準時
America/Buenos_Aires             アルゼンチン標準時
America/Cambridge_Bay            アメリカ山地標準時
America/Campo_Grande             アマゾン標準時
America/Cancun                   アメリカ中部標準時
America/Caracas                  ベネズエラ時間
America/Catamarca                アルゼンチン標準時
America/Cayenne                  仏領ギアナ時間
America/Cayman                   アメリカ東部標準時
America/Chicago                  アメリカ中部標準時
America/Chihuahua                メキシコ太平洋標準時
America/Coral_Harbour            アメリカ東部標準時
America/Cordoba                  アルゼンチン標準時
America/Costa_Rica               アメリカ中部標準時
America/Creston                  アメリカ山地標準時
America/Cuiaba                   アマゾン標準時
America/Curacao                  大西洋標準時
America/Danmarkshavn             グリニッジ標準時
America/Dawson                   アメリカ太平洋標準時
America/Dawson_Creek             アメリカ山地標準時
America/Denver                   アメリカ山地標準時
America/Detroit                  アメリカ東部標準時
America/Dominica                 大西洋標準時
America/Edmonton                 アメリカ山地標準時
America/Eirunepe                 アクレ標準時
America/El_Salvador              アメリカ中部標準時
America/Ensenada                 アメリカ太平洋標準時
America/Fort_Wayne               アメリカ東部標準時
America/Fortaleza                ブラジリア標準時
America/Glace_Bay                大西洋標準時
America/Godthab                  グリーンランド西部標準時
America/Goose_Bay                大西洋標準時
America/Grand_Turk               アメリカ東部標準時
America/Grenada                  大西洋標準時
America/Guadeloupe               大西洋標準時
America/Guatemala                アメリカ中部標準時
America/Guayaquil                エクアドル時間
America/Guyana                   ガイアナ時間
America/Halifax                  大西洋標準時
America/Havana                   キューバ標準時
America/Hermosillo               メキシコ太平洋標準時
America/Indiana/Indianapolis     アメリカ東部標準時
America/Indiana/Knox             アメリカ中部標準時
America/Indiana/Marengo          アメリカ東部標準時
America/Indiana/Petersburg       アメリカ東部標準時
America/Indiana/Tell_City        アメリカ中部標準時
America/Indiana/Vevay            アメリカ東部標準時
America/Indiana/Vincennes        アメリカ東部標準時
America/Indiana/Winamac          アメリカ東部標準時
America/Indianapolis             アメリカ東部標準時
America/Inuvik                   アメリカ山地標準時
America/Iqaluit                  アメリカ東部標準時
America/Jamaica                  アメリカ東部標準時
America/Jujuy                    アルゼンチン標準時
America/Juneau                   アラスカ標準時
America/Kentucky/Louisville      アメリカ東部標準時
America/Kentucky/Monticello      アメリカ東部標準時
America/Knox_IN                  アメリカ中部標準時
America/Kralendijk               大西洋標準時
America/La_Paz                   ボリビア時間
America/Lima                     ペルー標準時
America/Los_Angeles              アメリカ太平洋標準時
America/Louisville               アメリカ東部標準時
America/Lower_Princes            大西洋標準時
America/Maceio                   ブラジリア標準時
America/Managua                  アメリカ中部標準時
America/Manaus                   アマゾン標準時
America/Marigot                  大西洋標準時
America/Martinique               大西洋標準時
America/Matamoros                アメリカ中部標準時
America/Mazatlan                 メキシコ太平洋標準時
America/Mendoza                  アルゼンチン標準時
America/Menominee                アメリカ中部標準時
America/Merida                   アメリカ中部標準時
America/Metlakatla               アメリカ太平洋標準時
America/Mexico_City              アメリカ中部標準時
America/Miquelon                 サンピエール・ミクロン標準時
America/Moncton                  大西洋標準時
America/Monterrey                アメリカ中部標準時
America/Montevideo               ウルグアイ標準時
America/Montreal                 GMT-05:00
America/Montserrat               大西洋標準時
America/Nassau                   アメリカ東部標準時
America/New_York                 アメリカ東部標準時
America/Nipigon                  アメリカ東部標準時
America/Nome                     アラスカ標準時
America/Noronha                  フェルナンド・デ・ノローニャ標準時
America/North_Dakota/Beulah      アメリカ中部標準時
America/North_Dakota/Center      アメリカ中部標準時
America/North_Dakota/New_Salem   アメリカ中部標準時
America/Ojinaga                  アメリカ山地標準時
America/Panama                   アメリカ東部標準時
America/Pangnirtung              アメリカ東部標準時
America/Paramaribo               スリナム時間
America/Phoenix                  アメリカ山地標準時
America/Port-au-Prince           アメリカ東部標準時
America/Port_of_Spain            大西洋標準時
America/Porto_Acre               アクレ標準時
America/Porto_Velho              アマゾン標準時
America/Puerto_Rico              大西洋標準時
America/Rainy_River              アメリカ中部標準時
America/Rankin_Inlet             アメリカ中部標準時
America/Recife                   ブラジリア標準時
America/Regina                   アメリカ中部標準時
America/Resolute                 アメリカ中部標準時
America/Rio_Branco               アクレ標準時
America/Rosario                  アルゼンチン標準時
America/Santa_Isabel             メキシコ北西部標準時
America/Santarem                 ブラジリア標準時
America/Santiago                 チリ標準時
America/Santo_Domingo            大西洋標準時
America/Sao_Paulo                ブラジリア標準時
America/Scoresbysund             グリーンランド東部標準時
America/Shiprock                 アメリカ山地標準時
America/Sitka                    アラスカ標準時
America/St_Barthelemy            大西洋標準時
America/St_Johns                 ニューファンドランド標準時
America/St_Kitts                 大西洋標準時
America/St_Lucia                 大西洋標準時
America/St_Thomas                大西洋標準時
America/St_Vincent               大西洋標準時
America/Swift_Current            アメリカ中部標準時
America/Tegucigalpa              アメリカ中部標準時
America/Thule                    大西洋標準時
America/Thunder_Bay              アメリカ東部標準時
America/Tijuana                  アメリカ太平洋標準時
America/Toronto                  アメリカ東部標準時
America/Tortola                  大西洋標準時
America/Vancouver                アメリカ太平洋標準時
America/Virgin                   大西洋標準時
America/Whitehorse               アメリカ太平洋標準時
America/Winnipeg                 アメリカ中部標準時
America/Yakutat                  アラスカ標準時
America/Yellowknife              アメリカ山地標準時
Antarctica/Casey                 オーストラリア西部標準時
Antarctica/Davis                 デービス基地時間
Antarctica/DumontDUrville        デュモン・デュルヴィル基地時間
Antarctica/Macquarie             マッコーリー島時間
Antarctica/Mawson                モーソン基地時間
Antarctica/McMurdo               ニュージーランド標準時
Antarctica/Palmer                チリ標準時
Antarctica/Rothera               ロゼラ基地時間
Antarctica/South_Pole            ニュージーランド標準時
Antarctica/Syowa                 昭和基地時間
Antarctica/Troll                 グリニッジ標準時
Antarctica/Vostok                ボストーク基地時間
Arctic/Longyearbyen              中央ヨーロッパ標準時
Asia/Aden                        アラビア標準時
Asia/Almaty                      東カザフスタン時間
Asia/Amman                       東ヨーロッパ標準時
Asia/Anadyr                      マガダン標準時
Asia/Aqtau                       西カザフスタン時間
Asia/Aqtobe                      西カザフスタン時間
Asia/Ashgabat                    トルクメニスタン標準時
Asia/Ashkhabad                   トルクメニスタン標準時
Asia/Baghdad                     アラビア標準時
Asia/Bahrain                     アラビア標準時
Asia/Baku                        アゼルバイジャン標準時
Asia/Bangkok                     インドシナ時間
Asia/Beirut                      東ヨーロッパ標準時
Asia/Bishkek                     キルギスタン時間
Asia/Brunei                      ブルネイ・ダルサラーム時間
Asia/Calcutta                    インド標準時
Asia/Chita                       ヤクーツク標準時
Asia/Choibalsan                  チョイバルサン標準時
Asia/Chongqing                   中国標準時
Asia/Chungking                   中国標準時
Asia/Colombo                     インド標準時
Asia/Dacca                       バングラデシュ標準時
Asia/Damascus                    東ヨーロッパ標準時
Asia/Dhaka                       バングラデシュ標準時
Asia/Dili                        東ティモール時間
Asia/Dubai                       湾岸標準時
Asia/Dushanbe                    タジキスタン時間
Asia/Gaza                        東ヨーロッパ標準時
Asia/Harbin                      中国標準時
Asia/Hebron                      東ヨーロッパ標準時
Asia/Ho_Chi_Minh                 インドシナ時間
Asia/Hong_Kong                   香港標準時
Asia/Hovd                        ホブド標準時
Asia/Irkutsk                     イルクーツク標準時
Asia/Istanbul                    東ヨーロッパ標準時
Asia/Jakarta                     インドネシア西部時間
Asia/Jayapura                    インドネシア東部時間
Asia/Jerusalem                   イスラエル標準時
Asia/Kabul                       アフガニスタン時間
Asia/Kamchatka                   マガダン標準時
Asia/Karachi                     パキスタン標準時
Asia/Kashgar                     GMT+06:00
Asia/Kathmandu                   ネパール時間
Asia/Katmandu                    ネパール時間
Asia/Khandyga                    ヤクーツク標準時
Asia/Kolkata                     インド標準時
Asia/Krasnoyarsk                 クラスノヤルスク標準時
Asia/Kuala_Lumpur                マレーシア時間
Asia/Kuching                     マレーシア時間
Asia/Kuwait                      アラビア標準時
Asia/Macao                       中国標準時
Asia/Macau                       中国標準時
Asia/Magadan                     マガダン標準時
Asia/Makassar                    インドネシア中部時間
Asia/Manila                      フィリピン標準時
Asia/Muscat                      湾岸標準時
Asia/Nicosia                     東ヨーロッパ標準時
Asia/Novokuznetsk                ノヴォシビルスク標準時
Asia/Novosibirsk                 ノヴォシビルスク標準時
Asia/Omsk                        オムスク標準時
Asia/Oral                        西カザフスタン時間
Asia/Phnom_Penh                  インドシナ時間
Asia/Pontianak                   インドネシア西部時間
Asia/Pyongyang                   韓国標準時
Asia/Qatar                       アラビア標準時
Asia/Qyzylorda                   東カザフスタン時間
Asia/Rangoon                     ミャンマー時間
Asia/Riyadh                      アラビア標準時
Asia/Saigon                      インドシナ時間
Asia/Sakhalin                    サハリン標準時
Asia/Samarkand                   ウズベキスタン標準時
Asia/Seoul                       韓国標準時
Asia/Shanghai                    中国標準時
Asia/Singapore                   シンガポール標準時
Asia/Srednekolymsk               マガダン標準時
Asia/Taipei                      台北標準時
Asia/Tashkent                    ウズベキスタン標準時
Asia/Tbilisi                     グルジア標準時
Asia/Tehran                      イラン標準時
Asia/Tel_Aviv                    イスラエル標準時
Asia/Thimbu                      ブータン時間
Asia/Thimphu                     ブータン時間
Asia/Tokyo                       日本標準時
Asia/Ujung_Pandang               インドネシア中部時間
Asia/Ulaanbaatar                 ウランバートル標準時
Asia/Ulan_Bator                  ウランバートル標準時
Asia/Urumqi                      GMT+06:00
Asia/Ust-Nera                    ウラジオストク標準時
Asia/Vientiane                   インドシナ時間
Asia/Vladivostok                 ウラジオストク標準時
Asia/Yakutsk                     ヤクーツク標準時
Asia/Yekaterinburg               エカテリンブルグ標準時
Asia/Yerevan                     アルメニア標準時
Atlantic/Azores                  アゾレス標準時
Atlantic/Bermuda                 大西洋標準時
Atlantic/Canary                  西ヨーロッパ標準時
Atlantic/Cape_Verde              カーボベルデ標準時
Atlantic/Faeroe                  西ヨーロッパ標準時
Atlantic/Faroe                   西ヨーロッパ標準時
Atlantic/Jan_Mayen               中央ヨーロッパ標準時
Atlantic/Madeira                 西ヨーロッパ標準時
Atlantic/Reykjavik               グリニッジ標準時
Atlantic/South_Georgia           サウスジョージア時間
Atlantic/St_Helena               グリニッジ標準時
Atlantic/Stanley                 フォークランド諸島標準時
Australia/ACT                    オーストラリア東部標準時
Australia/Adelaide               オーストラリア中部標準時
Australia/Brisbane               オーストラリア東部標準時
Australia/Broken_Hill            オーストラリア中部標準時
Australia/Canberra               オーストラリア東部標準時
Australia/Currie                 オーストラリア東部標準時
Australia/Darwin                 オーストラリア中部標準時
Australia/Eucla                  オーストラリア中西部標準時
Australia/Hobart                 オーストラリア東部標準時
Australia/LHI                    ロードハウ標準時
Australia/Lindeman               オーストラリア東部標準時
Australia/Lord_Howe              ロードハウ標準時
Australia/Melbourne              オーストラリア東部標準時
Australia/NSW                    オーストラリア東部標準時
Australia/North                  オーストラリア中部標準時
Australia/Perth                  オーストラリア西部標準時
Australia/Queensland             オーストラリア東部標準時
Australia/South                  オーストラリア中部標準時
Australia/Sydney                 オーストラリア東部標準時
Australia/Tasmania               オーストラリア東部標準時
Australia/Victoria               オーストラリア東部標準時
Australia/West                   オーストラリア西部標準時
Australia/Yancowinna             オーストラリア中部標準時
BET                              ブラジリア標準時
BST                              バングラデシュ標準時
Brazil/Acre                      アクレ標準時
Brazil/DeNoronha                 フェルナンド・デ・ノローニャ標準時
Brazil/East                      ブラジリア標準時
Brazil/West                      アマゾン標準時
CAT                              中央アフリカ時間
CET                              GMT+01:00
CNT                              ニューファンドランド標準時
CST                              アメリカ中部標準時
CST6CDT                          アメリカ中部標準時
CTT                              中国標準時
Canada/Atlantic                  大西洋標準時
Canada/Central                   アメリカ中部標準時
Canada/East-Saskatchewan         アメリカ中部標準時
Canada/Eastern                   アメリカ東部標準時
Canada/Mountain                  アメリカ山地標準時
Canada/Newfoundland              ニューファンドランド標準時
Canada/Pacific                   アメリカ太平洋標準時
Canada/Saskatchewan              アメリカ中部標準時
Canada/Yukon                     アメリカ太平洋標準時
Chile/Continental                チリ標準時
Chile/EasterIsland               イースター島標準時
Cuba                             キューバ標準時
EAT                              東アフリカ時間
ECT                              中央ヨーロッパ標準時
EET                              GMT+02:00
EST                              GMT-05:00
EST5EDT                          アメリカ東部標準時
Egypt                            東ヨーロッパ標準時
Eire                             グリニッジ標準時
Etc/GMT                          GMT
Etc/GMT+0                        GMT
Etc/GMT+1                        GMT-01:00
Etc/GMT+10                       GMT-10:00
Etc/GMT+11                       GMT-11:00
Etc/GMT+12                       GMT-12:00
Etc/GMT+2                        GMT-02:00
Etc/GMT+3                        GMT-03:00
Etc/GMT+4                        GMT-04:00
Etc/GMT+5                        GMT-05:00
Etc/GMT+6                        GMT-06:00
Etc/GMT+7                        GMT-07:00
Etc/GMT+8                        GMT-08:00
Etc/GMT+9                        GMT-09:00
Etc/GMT-0                        GMT
Etc/GMT-1                        GMT+01:00
Etc/GMT-10                       GMT+10:00
Etc/GMT-11                       GMT+11:00
Etc/GMT-12                       GMT+12:00
Etc/GMT-13                       GMT+13:00
Etc/GMT-14                       GMT+14:00
Etc/GMT-2                        GMT+02:00
Etc/GMT-3                        GMT+03:00
Etc/GMT-4                        GMT+04:00
Etc/GMT-5                        GMT+05:00
Etc/GMT-6                        GMT+06:00
Etc/GMT-7                        GMT+07:00
Etc/GMT-8                        GMT+08:00
Etc/GMT-9                        GMT+09:00
Etc/GMT0                         GMT
Etc/Greenwich                    GMT
Etc/UCT                          GMT
Etc/UTC                          GMT
Etc/Universal                    GMT
Etc/Zulu                         GMT
Europe/Amsterdam                 中央ヨーロッパ標準時
Europe/Andorra                   中央ヨーロッパ標準時
Europe/Athens                    東ヨーロッパ標準時
Europe/Belfast                   グリニッジ標準時
Europe/Belgrade                  中央ヨーロッパ標準時
Europe/Berlin                    中央ヨーロッパ標準時
Europe/Bratislava                中央ヨーロッパ標準時
Europe/Brussels                  中央ヨーロッパ標準時
Europe/Bucharest                 東ヨーロッパ標準時
Europe/Budapest                  中央ヨーロッパ標準時
Europe/Busingen                  中央ヨーロッパ標準時
Europe/Chisinau                  東ヨーロッパ標準時
Europe/Copenhagen                中央ヨーロッパ標準時
Europe/Dublin                    グリニッジ標準時
Europe/Gibraltar                 中央ヨーロッパ標準時
Europe/Guernsey                  グリニッジ標準時
Europe/Helsinki                  東ヨーロッパ標準時
Europe/Isle_of_Man               グリニッジ標準時
Europe/Istanbul                  東ヨーロッパ標準時
Europe/Jersey                    グリニッジ標準時
Europe/Kaliningrad               極東ヨーロッパ時間
Europe/Kiev                      東ヨーロッパ標準時
Europe/Lisbon                    西ヨーロッパ標準時
Europe/Ljubljana                 中央ヨーロッパ標準時
Europe/London                    グリニッジ標準時
Europe/Luxembourg                中央ヨーロッパ標準時
Europe/Madrid                    中央ヨーロッパ標準時
Europe/Malta                     中央ヨーロッパ標準時
Europe/Mariehamn                 東ヨーロッパ標準時
Europe/Minsk                     極東ヨーロッパ時間
Europe/Monaco                    中央ヨーロッパ標準時
Europe/Moscow                    モスクワ標準時
Europe/Nicosia                   東ヨーロッパ標準時
Europe/Oslo                      中央ヨーロッパ標準時
Europe/Paris                     中央ヨーロッパ標準時
Europe/Podgorica                 中央ヨーロッパ標準時
Europe/Prague                    中央ヨーロッパ標準時
Europe/Riga                      東ヨーロッパ標準時
Europe/Rome                      中央ヨーロッパ標準時
Europe/Samara                    モスクワ標準時
Europe/San_Marino                中央ヨーロッパ標準時
Europe/Sarajevo                  中央ヨーロッパ標準時
Europe/Simferopol                モスクワ標準時
Europe/Skopje                    中央ヨーロッパ標準時
Europe/Sofia                     東ヨーロッパ標準時
Europe/Stockholm                 中央ヨーロッパ標準時
Europe/Tallinn                   東ヨーロッパ標準時
Europe/Tirane                    中央ヨーロッパ標準時
Europe/Tiraspol                  東ヨーロッパ標準時
Europe/Uzhgorod                  東ヨーロッパ標準時
Europe/Vaduz                     中央ヨーロッパ標準時
Europe/Vatican                   中央ヨーロッパ標準時
Europe/Vienna                    中央ヨーロッパ標準時
Europe/Vilnius                   東ヨーロッパ標準時
Europe/Volgograd                 モスクワ標準時
Europe/Warsaw                    中央ヨーロッパ標準時
Europe/Zagreb                    中央ヨーロッパ標準時
Europe/Zaporozhye                東ヨーロッパ標準時
Europe/Zurich                    中央ヨーロッパ標準時
Factory                          GMT
GB                               グリニッジ標準時
GB-Eire                          グリニッジ標準時
GMT                              GMT
GMT+0                            GMT
GMT-0                            GMT
GMT0                             GMT
Greenwich                        GMT
HST                              GMT-10:00
Hongkong                         香港標準時
IET                              アメリカ東部標準時
IST                              インド標準時
Iceland                          グリニッジ標準時
Indian/Antananarivo              東アフリカ時間
Indian/Chagos                    インド洋時間
Indian/Christmas                 クリスマス島時間
Indian/Cocos                     ココス諸島時間
Indian/Comoro                    東アフリカ時間
Indian/Kerguelen                 仏領南方南極時間
Indian/Mahe                      セーシェル時間
Indian/Maldives                  モルディブ時間
Indian/Mauritius                 モーリシャス標準時
Indian/Mayotte                   東アフリカ時間
Indian/Reunion                   レユニオン時間
Iran                             イラン標準時
Israel                           イスラエル標準時
JST                              日本標準時
Jamaica                          アメリカ東部標準時
Japan                            日本標準時
Kwajalein                        マーシャル諸島時間
Libya                            東ヨーロッパ標準時
MET                              GMT+01:00
MIT                              アピーア標準時
MST                              GMT-07:00
MST7MDT                          アメリカ山地標準時
Mexico/BajaNorte                 アメリカ太平洋標準時
Mexico/BajaSur                   メキシコ太平洋標準時
Mexico/General                   アメリカ中部標準時
NET                              アルメニア標準時
NST                              ニュージーランド標準時
NZ                               ニュージーランド標準時
NZ-CHAT                          チャタム標準時
Navajo                           アメリカ山地標準時
PLT                              パキスタン標準時
PNT                              アメリカ山地標準時
PRC                              中国標準時
PRT                              大西洋標準時
PST                              アメリカ太平洋標準時
PST8PDT                          アメリカ太平洋標準時
Pacific/Apia                     アピーア標準時
Pacific/Auckland                 ニュージーランド標準時
Pacific/Chatham                  チャタム標準時
Pacific/Chuuk                    チューク時間
Pacific/Easter                   イースター島標準時
Pacific/Efate                    バヌアツ標準時
Pacific/Enderbury                フェニックス諸島時間
Pacific/Fakaofo                  トケラウ時間
Pacific/Fiji                     フィジー標準時
Pacific/Funafuti                 ツバル時間
Pacific/Galapagos                ガラパゴス時間
Pacific/Gambier                  ガンビエ諸島時間
Pacific/Guadalcanal              ソロモン諸島時間
Pacific/Guam                     チャモロ時間
Pacific/Honolulu                 ハワイ・アリューシャン標準時
Pacific/Johnston                 ハワイ・アリューシャン標準時
Pacific/Kiritimati               ライン諸島時間
Pacific/Kosrae                   コスラエ時間
Pacific/Kwajalein                マーシャル諸島時間
Pacific/Majuro                   マーシャル諸島時間
Pacific/Marquesas                マルキーズ時間
Pacific/Midway                   サモア標準時
Pacific/Nauru                    ナウル時間
Pacific/Niue                     ニウエ時間
Pacific/Norfolk                  ノーフォーク島時間
Pacific/Noumea                   ニューカレドニア標準時
Pacific/Pago_Pago                サモア標準時
Pacific/Palau                    パラオ時間
Pacific/Pitcairn                 ピトケアン時間
Pacific/Pohnpei                  ポナペ時間
Pacific/Ponape                   ポナペ時間
Pacific/Port_Moresby             パプアニューギニア時間
Pacific/Rarotonga                クック諸島標準時
Pacific/Saipan                   チャモロ時間
Pacific/Samoa                    サモア標準時
Pacific/Tahiti                   タヒチ時間
Pacific/Tarawa                   ギルバート諸島時間
Pacific/Tongatapu                トンガ標準時
Pacific/Truk                     チューク時間
Pacific/Wake                     ウェーク島時間
Pacific/Wallis                   ウォリス・フツナ時間
Pacific/Yap                      チューク時間
Poland                           中央ヨーロッパ標準時
Portugal                         西ヨーロッパ標準時
ROC                              台北標準時
ROK                              韓国標準時
SST                              ソロモン諸島時間
Singapore                        シンガポール標準時
SystemV/AST4                     GMT-04:00
SystemV/AST4ADT                  GMT-04:00
SystemV/CST6                     GMT-06:00
SystemV/CST6CDT                  GMT-06:00
SystemV/EST5                     GMT-05:00
SystemV/EST5EDT                  GMT-05:00
SystemV/HST10                    GMT-10:00
SystemV/MST7                     GMT-07:00
SystemV/MST7MDT                  GMT-07:00
SystemV/PST8                     GMT-08:00
SystemV/PST8PDT                  GMT-08:00
SystemV/YST9                     GMT-09:00
SystemV/YST9YDT                  GMT-09:00
Turkey                           東ヨーロッパ標準時
UCT                              GMT
US/Alaska                        アラスカ標準時
US/Aleutian                      ハワイ・アリューシャン標準時
US/Arizona                       アメリカ山地標準時
US/Central                       アメリカ中部標準時
US/East-Indiana                  アメリカ東部標準時
US/Eastern                       アメリカ東部標準時
US/Hawaii                        ハワイ・アリューシャン標準時
US/Indiana-Starke                アメリカ中部標準時
US/Michigan                      アメリカ東部標準時
US/Mountain                      アメリカ山地標準時
US/Pacific                       アメリカ太平洋標準時
US/Pacific-New                   アメリカ太平洋標準時
US/Samoa                         サモア標準時
UTC                              GMT
Universal                        GMT
VST                              インドシナ時間
W-SU                             モスクワ標準時
WET                              GMT
Zulu                             GMT

各言語での日本時間の表記

Asia/Tokyo af          Japan-standaardtyd
Asia/Tokyo af_NA       Japan-standaardtyd
Asia/Tokyo af_ZA       Japan-standaardtyd
Asia/Tokyo agq         GMT+09:00
Asia/Tokyo agq_CM      GMT+09:00
Asia/Tokyo ak          GMT+09:00
Asia/Tokyo ak_GH       GMT+09:00
Asia/Tokyo am          የጃፓን መደበኛ ሰዓት
Asia/Tokyo am_ET       የጃፓን መደበኛ ሰዓት
Asia/Tokyo ar          توقيت اليابان الرسمي
Asia/Tokyo ar_001      توقيت اليابان الرسمي
Asia/Tokyo ar_AE       توقيت اليابان الرسمي
Asia/Tokyo ar_BH       توقيت اليابان الرسمي
Asia/Tokyo ar_DJ       توقيت اليابان الرسمي
Asia/Tokyo ar_DZ       توقيت اليابان الرسمي
Asia/Tokyo ar_EG       توقيت اليابان الرسمي
Asia/Tokyo ar_EH       توقيت اليابان الرسمي
Asia/Tokyo ar_ER       توقيت اليابان الرسمي
Asia/Tokyo ar_IL       توقيت اليابان الرسمي
Asia/Tokyo ar_IQ       توقيت اليابان الرسمي
Asia/Tokyo ar_JO       توقيت اليابان الرسمي
Asia/Tokyo ar_KM       توقيت اليابان الرسمي
Asia/Tokyo ar_KW       توقيت اليابان الرسمي
Asia/Tokyo ar_LB       توقيت اليابان الرسمي
Asia/Tokyo ar_LY       توقيت اليابان الرسمي
Asia/Tokyo ar_MA       توقيت اليابان الرسمي
Asia/Tokyo ar_MR       توقيت اليابان الرسمي
Asia/Tokyo ar_OM       توقيت اليابان الرسمي
Asia/Tokyo ar_PS       توقيت اليابان الرسمي
Asia/Tokyo ar_QA       توقيت اليابان الرسمي
Asia/Tokyo ar_SA       توقيت اليابان الرسمي
Asia/Tokyo ar_SD       توقيت اليابان الرسمي
Asia/Tokyo ar_SO       توقيت اليابان الرسمي
Asia/Tokyo ar_SS       توقيت اليابان الرسمي
Asia/Tokyo ar_SY       توقيت اليابان الرسمي
Asia/Tokyo ar_TD       توقيت اليابان الرسمي
Asia/Tokyo ar_TN       توقيت اليابان الرسمي
Asia/Tokyo ar_YE       توقيت اليابان الرسمي
Asia/Tokyo as          GMT+০৯:০০
Asia/Tokyo as_IN       GMT+০৯:০০
Asia/Tokyo asa         GMT+09:00
Asia/Tokyo asa_TZ      GMT+09:00
Asia/Tokyo az          Yaponiya Standart Vaxtı
Asia/Tokyo az_Cyrl     GMT+09:00
Asia/Tokyo az_Cyrl_AZ  GMT+09:00
Asia/Tokyo az_Latn     Yaponiya Standart Vaxtı
Asia/Tokyo az_Latn_AZ  Yaponiya Standart Vaxtı
Asia/Tokyo bas         GMT+09:00
Asia/Tokyo bas_CM      GMT+09:00
Asia/Tokyo be          GMT+09:00
Asia/Tokyo be_BY       GMT+09:00
Asia/Tokyo bem         GMT+09:00
Asia/Tokyo bem_ZM      GMT+09:00
Asia/Tokyo bez         GMT+09:00
Asia/Tokyo bez_TZ      GMT+09:00
Asia/Tokyo bg          Японско стандартно време
Asia/Tokyo bg_BG       Японско стандартно време
Asia/Tokyo bm          GMT+09:00
Asia/Tokyo bm_Latn     GMT+09:00
Asia/Tokyo bm_Latn_ML  GMT+09:00
Asia/Tokyo bn          জাপান মানক সময়
Asia/Tokyo bn_BD       জাপান মানক সময়
Asia/Tokyo bn_IN       জাপান মানক সময়
Asia/Tokyo bo          GMT+09:00
Asia/Tokyo bo_CN       GMT+09:00
Asia/Tokyo bo_IN       GMT+09:00
Asia/Tokyo br          GMT+09:00
Asia/Tokyo br_FR       GMT+09:00
Asia/Tokyo brx         जपान स्टैंडर्ड टाईम
Asia/Tokyo brx_IN      जपान स्टैंडर्ड टाईम
Asia/Tokyo bs          Standardno japansko vrijeme
Asia/Tokyo bs_Cyrl     Јапанско стандардно време
Asia/Tokyo bs_Cyrl_BA  Јапанско стандардно време
Asia/Tokyo bs_Latn     Standardno japansko vrijeme
Asia/Tokyo bs_Latn_BA  Standardno japansko vrijeme
Asia/Tokyo ca          Hora estàndard del Japó
Asia/Tokyo ca_AD       Hora estàndard del Japó
Asia/Tokyo ca_ES       Hora estàndard del Japó
Asia/Tokyo ca_FR       Hora estàndard del Japó
Asia/Tokyo ca_IT       Hora estàndard del Japó
Asia/Tokyo cgg         GMT+09:00
Asia/Tokyo cgg_UG      GMT+09:00
Asia/Tokyo chr         GMT+09:00
Asia/Tokyo chr_US      GMT+09:00
Asia/Tokyo cs          Japonský standardní čas
Asia/Tokyo cs_CZ       Japonský standardní čas
Asia/Tokyo cy          Amser Safonol Japan
Asia/Tokyo cy_GB       Amser Safonol Japan
Asia/Tokyo da          Japansk normaltid
Asia/Tokyo da_DK       Japansk normaltid
Asia/Tokyo da_GL       Japansk normaltid
Asia/Tokyo dav         GMT+09:00
Asia/Tokyo dav_KE      GMT+09:00
Asia/Tokyo de          Japanische Normalzeit
Asia/Tokyo de_AT       Japanische Normalzeit
Asia/Tokyo de_BE       Japanische Normalzeit
Asia/Tokyo de_CH       Japanische Normalzeit
Asia/Tokyo de_DE       Japanische Normalzeit
Asia/Tokyo de_LI       Japanische Normalzeit
Asia/Tokyo de_LU       Japanische Normalzeit
Asia/Tokyo dje         GMT+09:00
Asia/Tokyo dje_NE      GMT+09:00
Asia/Tokyo dsb         Japański standardny cas
Asia/Tokyo dsb_DE      Japański standardny cas
Asia/Tokyo dua         GMT+09:00
Asia/Tokyo dua_CM      GMT+09:00
Asia/Tokyo dyo         GMT+09:00
Asia/Tokyo dyo_SN      GMT+09:00
Asia/Tokyo dz          ཇ་པཱན་ཚད་ལྡན་ཆུ་ཚོད
Asia/Tokyo dz_BT       ཇ་པཱན་ཚད་ལྡན་ཆུ་ཚོད
Asia/Tokyo ebu         GMT+09:00
Asia/Tokyo ebu_KE      GMT+09:00
Asia/Tokyo ee          GMT+09:00
Asia/Tokyo ee_GH       GMT+09:00
Asia/Tokyo ee_TG       GMT+09:00
Asia/Tokyo el          Χειμερινή ώρα Ιαπωνίας
Asia/Tokyo el_CY       Χειμερινή ώρα Ιαπωνίας
Asia/Tokyo el_GR       Χειμερινή ώρα Ιαπωνίας
Asia/Tokyo en          Japan Standard Time
Asia/Tokyo en_001      Japan Standard Time
Asia/Tokyo en_150      Japan Standard Time
Asia/Tokyo en_AG       Japan Standard Time
Asia/Tokyo en_AI       Japan Standard Time
Asia/Tokyo en_AS       Japan Standard Time
Asia/Tokyo en_AU       Japan Standard Time
Asia/Tokyo en_BB       Japan Standard Time
Asia/Tokyo en_BE       Japan Standard Time
Asia/Tokyo en_BM       Japan Standard Time
Asia/Tokyo en_BS       Japan Standard Time
Asia/Tokyo en_BW       Japan Standard Time
Asia/Tokyo en_BZ       Japan Standard Time
Asia/Tokyo en_CA       Japan Standard Time
Asia/Tokyo en_CC       Japan Standard Time
Asia/Tokyo en_CK       Japan Standard Time
Asia/Tokyo en_CM       Japan Standard Time
Asia/Tokyo en_CX       Japan Standard Time
Asia/Tokyo en_DG       Japan Standard Time
Asia/Tokyo en_DM       Japan Standard Time
Asia/Tokyo en_ER       Japan Standard Time
Asia/Tokyo en_FJ       Japan Standard Time
Asia/Tokyo en_FK       Japan Standard Time
Asia/Tokyo en_FM       Japan Standard Time
Asia/Tokyo en_GB       Japan Standard Time
Asia/Tokyo en_GD       Japan Standard Time
Asia/Tokyo en_GG       Japan Standard Time
Asia/Tokyo en_GH       Japan Standard Time
Asia/Tokyo en_GI       Japan Standard Time
Asia/Tokyo en_GM       Japan Standard Time
Asia/Tokyo en_GU       Japan Standard Time
Asia/Tokyo en_GY       Japan Standard Time
Asia/Tokyo en_HK       Japan Standard Time
Asia/Tokyo en_IE       Japan Standard Time
Asia/Tokyo en_IM       Japan Standard Time
Asia/Tokyo en_IN       Japan Standard Time
Asia/Tokyo en_IO       Japan Standard Time
Asia/Tokyo en_JE       Japan Standard Time
Asia/Tokyo en_JM       Japan Standard Time
Asia/Tokyo en_KE       Japan Standard Time
Asia/Tokyo en_KI       Japan Standard Time
Asia/Tokyo en_KN       Japan Standard Time
Asia/Tokyo en_KY       Japan Standard Time
Asia/Tokyo en_LC       Japan Standard Time
Asia/Tokyo en_LR       Japan Standard Time
Asia/Tokyo en_LS       Japan Standard Time
Asia/Tokyo en_MG       Japan Standard Time
Asia/Tokyo en_MH       Japan Standard Time
Asia/Tokyo en_MO       Japan Standard Time
Asia/Tokyo en_MP       Japan Standard Time
Asia/Tokyo en_MS       Japan Standard Time
Asia/Tokyo en_MT       Japan Standard Time
Asia/Tokyo en_MU       Japan Standard Time
Asia/Tokyo en_MW       Japan Standard Time
Asia/Tokyo en_MY       Japan Standard Time
Asia/Tokyo en_NA       Japan Standard Time
Asia/Tokyo en_NF       Japan Standard Time
Asia/Tokyo en_NG       Japan Standard Time
Asia/Tokyo en_NR       Japan Standard Time
Asia/Tokyo en_NU       Japan Standard Time
Asia/Tokyo en_NZ       Japan Standard Time
Asia/Tokyo en_PG       Japan Standard Time
Asia/Tokyo en_PH       Japan Standard Time
Asia/Tokyo en_PK       Japan Standard Time
Asia/Tokyo en_PN       Japan Standard Time
Asia/Tokyo en_PR       Japan Standard Time
Asia/Tokyo en_PW       Japan Standard Time
Asia/Tokyo en_RW       Japan Standard Time
Asia/Tokyo en_SB       Japan Standard Time
Asia/Tokyo en_SC       Japan Standard Time
Asia/Tokyo en_SD       Japan Standard Time
Asia/Tokyo en_SG       Japan Standard Time
Asia/Tokyo en_SH       Japan Standard Time
Asia/Tokyo en_SL       Japan Standard Time
Asia/Tokyo en_SS       Japan Standard Time
Asia/Tokyo en_SX       Japan Standard Time
Asia/Tokyo en_SZ       Japan Standard Time
Asia/Tokyo en_TC       Japan Standard Time
Asia/Tokyo en_TK       Japan Standard Time
Asia/Tokyo en_TO       Japan Standard Time
Asia/Tokyo en_TT       Japan Standard Time
Asia/Tokyo en_TV       Japan Standard Time
Asia/Tokyo en_TZ       Japan Standard Time
Asia/Tokyo en_UG       Japan Standard Time
Asia/Tokyo en_UM       Japan Standard Time
Asia/Tokyo en_US       Japan Standard Time
Asia/Tokyo en_US_POSIX Japan Standard Time
Asia/Tokyo en_VC       Japan Standard Time
Asia/Tokyo en_VG       Japan Standard Time
Asia/Tokyo en_VI       Japan Standard Time
Asia/Tokyo en_VU       Japan Standard Time
Asia/Tokyo en_WS       Japan Standard Time
Asia/Tokyo en_ZA       Japan Standard Time
Asia/Tokyo en_ZM       Japan Standard Time
Asia/Tokyo en_ZW       Japan Standard Time
Asia/Tokyo eo          GMT+09:00
Asia/Tokyo es          hora estándar de Japón
Asia/Tokyo es_419      hora estándar de Japón
Asia/Tokyo es_AR       hora estándar de Japón
Asia/Tokyo es_BO       hora estándar de Japón
Asia/Tokyo es_CL       hora estándar de Japón
Asia/Tokyo es_CO       hora estándar de Japón
Asia/Tokyo es_CR       hora estándar de Japón
Asia/Tokyo es_CU       hora estándar de Japón
Asia/Tokyo es_DO       hora estándar de Japón
Asia/Tokyo es_EA       hora estándar de Japón
Asia/Tokyo es_EC       hora estándar de Japón
Asia/Tokyo es_ES       hora estándar de Japón
Asia/Tokyo es_GQ       hora estándar de Japón
Asia/Tokyo es_GT       hora estándar de Japón
Asia/Tokyo es_HN       hora estándar de Japón
Asia/Tokyo es_IC       hora estándar de Japón
Asia/Tokyo es_MX       Hora estándar de Japón
Asia/Tokyo es_NI       hora estándar de Japón
Asia/Tokyo es_PA       hora estándar de Japón
Asia/Tokyo es_PE       hora estándar de Japón
Asia/Tokyo es_PH       hora estándar de Japón
Asia/Tokyo es_PR       hora estándar de Japón
Asia/Tokyo es_PY       hora estándar de Japón
Asia/Tokyo es_SV       hora estándar de Japón
Asia/Tokyo es_US       hora estándar de Japón
Asia/Tokyo es_UY       hora estándar de Japón
Asia/Tokyo es_VE       hora estándar de Japón
Asia/Tokyo et          Jaapani standardaeg
Asia/Tokyo et_EE       Jaapani standardaeg
Asia/Tokyo eu          Japoniako ordu estandarra
Asia/Tokyo eu_ES       Japoniako ordu estandarra
Asia/Tokyo ewo         GMT+09:00
Asia/Tokyo ewo_CM      GMT+09:00
Asia/Tokyo fa          وقت عادی ژاپن
Asia/Tokyo fa_AF       وقت عادی ژاپن
Asia/Tokyo fa_IR       وقت عادی ژاپن
Asia/Tokyo ff          GMT+09:00
Asia/Tokyo ff_CM       GMT+09:00
Asia/Tokyo ff_GN       GMT+09:00
Asia/Tokyo ff_MR       GMT+09:00
Asia/Tokyo ff_SN       GMT+09:00
Asia/Tokyo fi          Japanin normaaliaika
Asia/Tokyo fi_FI       Japanin normaaliaika
Asia/Tokyo fil         Standard Time ng Japan
Asia/Tokyo fil_PH      Standard Time ng Japan
Asia/Tokyo fo          GMT+09:00
Asia/Tokyo fo_FO       GMT+09:00
Asia/Tokyo fr          heure normale du Japon
Asia/Tokyo fr_BE       heure normale du Japon
Asia/Tokyo fr_BF       heure normale du Japon
Asia/Tokyo fr_BI       heure normale du Japon
Asia/Tokyo fr_BJ       heure normale du Japon
Asia/Tokyo fr_BL       heure normale du Japon
Asia/Tokyo fr_CA       heure normale du Japon
Asia/Tokyo fr_CD       heure normale du Japon
Asia/Tokyo fr_CF       heure normale du Japon
Asia/Tokyo fr_CG       heure normale du Japon
Asia/Tokyo fr_CH       heure normale du Japon
Asia/Tokyo fr_CI       heure normale du Japon
Asia/Tokyo fr_CM       heure normale du Japon
Asia/Tokyo fr_DJ       heure normale du Japon
Asia/Tokyo fr_DZ       heure normale du Japon
Asia/Tokyo fr_FR       heure normale du Japon
Asia/Tokyo fr_GA       heure normale du Japon
Asia/Tokyo fr_GF       heure normale du Japon
Asia/Tokyo fr_GN       heure normale du Japon
Asia/Tokyo fr_GP       heure normale du Japon
Asia/Tokyo fr_GQ       heure normale du Japon
Asia/Tokyo fr_HT       heure normale du Japon
Asia/Tokyo fr_KM       heure normale du Japon
Asia/Tokyo fr_LU       heure normale du Japon
Asia/Tokyo fr_MA       heure normale du Japon
Asia/Tokyo fr_MC       heure normale du Japon
Asia/Tokyo fr_MF       heure normale du Japon
Asia/Tokyo fr_MG       heure normale du Japon
Asia/Tokyo fr_ML       heure normale du Japon
Asia/Tokyo fr_MQ       heure normale du Japon
Asia/Tokyo fr_MR       heure normale du Japon
Asia/Tokyo fr_MU       heure normale du Japon
Asia/Tokyo fr_NC       heure normale du Japon
Asia/Tokyo fr_NE       heure normale du Japon
Asia/Tokyo fr_PF       heure normale du Japon
Asia/Tokyo fr_PM       heure normale du Japon
Asia/Tokyo fr_RE       heure normale du Japon
Asia/Tokyo fr_RW       heure normale du Japon
Asia/Tokyo fr_SC       heure normale du Japon
Asia/Tokyo fr_SN       heure normale du Japon
Asia/Tokyo fr_SY       heure normale du Japon
Asia/Tokyo fr_TD       heure normale du Japon
Asia/Tokyo fr_TG       heure normale du Japon
Asia/Tokyo fr_TN       heure normale du Japon
Asia/Tokyo fr_VU       heure normale du Japon
Asia/Tokyo fr_WF       heure normale du Japon
Asia/Tokyo fr_YT       heure normale du Japon
Asia/Tokyo fur         GMT+09:00
Asia/Tokyo fur_IT      GMT+09:00
Asia/Tokyo fy          Japanske standerttiid
Asia/Tokyo fy_NL       Japanske standerttiid
Asia/Tokyo ga          Am Caighdeánach na Seapáine
Asia/Tokyo ga_IE       Am Caighdeánach na Seapáine
Asia/Tokyo gd          Bun-àm na Seapaine
Asia/Tokyo gd_GB       Bun-àm na Seapaine
Asia/Tokyo gl          Horario estándar de Xapón
Asia/Tokyo gl_ES       Horario estándar de Xapón
Asia/Tokyo gsw         GMT+09:00
Asia/Tokyo gsw_CH      GMT+09:00
Asia/Tokyo gsw_FR      GMT+09:00
Asia/Tokyo gsw_LI      GMT+09:00
Asia/Tokyo gu          જાપાન માનક સમય
Asia/Tokyo gu_IN       જાપાન માનક સમય
Asia/Tokyo guz         GMT+09:00
Asia/Tokyo guz_KE      GMT+09:00
Asia/Tokyo gv          GMT+09:00
Asia/Tokyo gv_IM       GMT+09:00
Asia/Tokyo ha          GMT+09:00
Asia/Tokyo ha_Latn     GMT+09:00
Asia/Tokyo ha_Latn_GH  GMT+09:00
Asia/Tokyo ha_Latn_NE  GMT+09:00
Asia/Tokyo ha_Latn_NG  GMT+09:00
Asia/Tokyo haw         GMT+09:00
Asia/Tokyo haw_US      GMT+09:00
Asia/Tokyo he          שעון יפן (חורף)
Asia/Tokyo he_IL       שעון יפן (חורף)
Asia/Tokyo hi          जापान मानक समय
Asia/Tokyo hi_IN       जापान मानक समय
Asia/Tokyo hr          japansko standardno vrijeme
Asia/Tokyo hr_BA       japansko standardno vrijeme
Asia/Tokyo hr_HR       japansko standardno vrijeme
Asia/Tokyo hsb         japanski standardny čas
Asia/Tokyo hsb_DE      japanski standardny čas
Asia/Tokyo hu          japán téli idő
Asia/Tokyo hu_HU       japán téli idő
Asia/Tokyo hy          Ճապոնիայի ստանդարտ ժամանակ
Asia/Tokyo hy_AM       Ճապոնիայի ստանդարտ ժամանակ
Asia/Tokyo id          Waktu Standar Jepang
Asia/Tokyo id_ID       Waktu Standar Jepang
Asia/Tokyo ig          GMT+09:00
Asia/Tokyo ig_NG       GMT+09:00
Asia/Tokyo ii          GMT+09:00
Asia/Tokyo ii_CN       GMT+09:00
Asia/Tokyo is          Staðaltími í Japan
Asia/Tokyo is_IS       Staðaltími í Japan
Asia/Tokyo it          Ora standard del Giappone
Asia/Tokyo it_CH       Ora standard del Giappone
Asia/Tokyo it_IT       Ora standard del Giappone
Asia/Tokyo it_SM       Ora standard del Giappone
Asia/Tokyo ja          日本標準時
Asia/Tokyo ja_JP       日本標準時
Asia/Tokyo jgo         GMT+09:00
Asia/Tokyo jgo_CM      GMT+09:00
Asia/Tokyo jmc         GMT+09:00
Asia/Tokyo jmc_TZ      GMT+09:00
Asia/Tokyo ka          იაპონიის სტანდარტული დრო
Asia/Tokyo ka_GE       იაპონიის სტანდარტული დრო
Asia/Tokyo kab         GMT+09:00
Asia/Tokyo kab_DZ      GMT+09:00
Asia/Tokyo kam         GMT+09:00
Asia/Tokyo kam_KE      GMT+09:00
Asia/Tokyo kde         GMT+09:00
Asia/Tokyo kde_TZ      GMT+09:00
Asia/Tokyo kea         GMT+09:00
Asia/Tokyo kea_CV      GMT+09:00
Asia/Tokyo khq         GMT+09:00
Asia/Tokyo khq_ML      GMT+09:00
Asia/Tokyo ki          GMT+09:00
Asia/Tokyo ki_KE       GMT+09:00
Asia/Tokyo kk          Жапония стандартты уақыты
Asia/Tokyo kk_Cyrl     Жапония стандартты уақыты
Asia/Tokyo kk_Cyrl_KZ  Жапония стандартты уақыты
Asia/Tokyo kkj         GMT+09:00
Asia/Tokyo kkj_CM      GMT+09:00
Asia/Tokyo kl          GMT+09:00
Asia/Tokyo kl_GL       GMT+09:00
Asia/Tokyo kln         GMT+09:00
Asia/Tokyo kln_KE      GMT+09:00
Asia/Tokyo km          ម៉ោង​ស្តង់ដារ​នៅ​ជប៉ុន
Asia/Tokyo km_KH       ម៉ោង​ស្តង់ដារ​នៅ​ជប៉ុន
Asia/Tokyo kn          ಜಪಾನ್ ಪ್ರಮಾಣಿತ ಸಮಯ
Asia/Tokyo kn_IN       ಜಪಾನ್ ಪ್ರಮಾಣಿತ ಸಮಯ
Asia/Tokyo ko          일본 표준시
Asia/Tokyo ko_KP       일본 표준시
Asia/Tokyo ko_KR       일본 표준시
Asia/Tokyo kok         GMT+09:00
Asia/Tokyo kok_IN      GMT+09:00
Asia/Tokyo ks          جاپٲنۍ سٹینڑاڑ ٹایِم
Asia/Tokyo ks_Arab     جاپٲنۍ سٹینڑاڑ ٹایِم
Asia/Tokyo ks_Arab_IN  جاپٲنۍ سٹینڑاڑ ٹایِم
Asia/Tokyo ksb         GMT+09:00
Asia/Tokyo ksb_TZ      GMT+09:00
Asia/Tokyo ksf         GMT+09:00
Asia/Tokyo ksf_CM      GMT+09:00
Asia/Tokyo ksh         GMT+09:00
Asia/Tokyo ksh_DE      GMT+09:00
Asia/Tokyo kw          GMT+09:00
Asia/Tokyo kw_GB       GMT+09:00
Asia/Tokyo ky          Жапан стандарт убактысы
Asia/Tokyo ky_Cyrl     Жапан стандарт убактысы
Asia/Tokyo ky_Cyrl_KG  Жапан стандарт убактысы
Asia/Tokyo lag         GMT+09:00
Asia/Tokyo lag_TZ      GMT+09:00
Asia/Tokyo lb          Japanesch Normalzäit
Asia/Tokyo lb_LU       Japanesch Normalzäit
Asia/Tokyo lg          GMT+09:00
Asia/Tokyo lg_UG       GMT+09:00
Asia/Tokyo lkt         GMT+09:00
Asia/Tokyo lkt_US      GMT+09:00
Asia/Tokyo ln          GMT+09:00
Asia/Tokyo ln_AO       GMT+09:00
Asia/Tokyo ln_CD       GMT+09:00
Asia/Tokyo ln_CF       GMT+09:00
Asia/Tokyo ln_CG       GMT+09:00
Asia/Tokyo lo          ເວ​ລາ​ມາດ​ຕະ​ຖານ​ຍີ່​ປຸ່ນ
Asia/Tokyo lo_LA       ເວ​ລາ​ມາດ​ຕະ​ຖານ​ຍີ່​ປຸ່ນ
Asia/Tokyo lt          Japonijos žiemos laikas
Asia/Tokyo lt_LT       Japonijos žiemos laikas
Asia/Tokyo lu          GMT+09:00
Asia/Tokyo lu_CD       GMT+09:00
Asia/Tokyo luo         GMT+09:00
Asia/Tokyo luo_KE      GMT+09:00
Asia/Tokyo luy         GMT+09:00
Asia/Tokyo luy_KE      GMT+09:00
Asia/Tokyo lv          Japānas ziemas laiks
Asia/Tokyo lv_LV       Japānas ziemas laiks
Asia/Tokyo mas         GMT+09:00
Asia/Tokyo mas_KE      GMT+09:00
Asia/Tokyo mas_TZ      GMT+09:00
Asia/Tokyo mer         GMT+09:00
Asia/Tokyo mer_KE      GMT+09:00
Asia/Tokyo mfe         GMT+09:00
Asia/Tokyo mfe_MU      GMT+09:00
Asia/Tokyo mg          GMT+09:00
Asia/Tokyo mg_MG       GMT+09:00
Asia/Tokyo mgh         GMT+09:00
Asia/Tokyo mgh_MZ      GMT+09:00
Asia/Tokyo mgo         GMT+09:00
Asia/Tokyo mgo_CM      GMT+09:00
Asia/Tokyo mk          Стандардно време во Јапонија
Asia/Tokyo mk_MK       Стандардно време во Јапонија
Asia/Tokyo ml          ജപ്പാൻ സ്റ്റാൻഡേർഡ് സമയം
Asia/Tokyo ml_IN       ജപ്പാൻ സ്റ്റാൻഡേർഡ് സമയം
Asia/Tokyo mn          Японы стандарт цаг
Asia/Tokyo mn_Cyrl     Японы стандарт цаг
Asia/Tokyo mn_Cyrl_MN  Японы стандарт цаг
Asia/Tokyo mr          जपान प्रमाण वेळ
Asia/Tokyo mr_IN       जपान प्रमाण वेळ
Asia/Tokyo ms          Waktu Piawai Jepun
Asia/Tokyo ms_Latn     Waktu Piawai Jepun
Asia/Tokyo ms_Latn_BN  Waktu Piawai Jepun
Asia/Tokyo ms_Latn_MY  Waktu Piawai Jepun
Asia/Tokyo ms_Latn_SG  Waktu Piawai Jepun
Asia/Tokyo mt          GMT+09:00
Asia/Tokyo mt_MT       GMT+09:00
Asia/Tokyo mua         GMT+09:00
Asia/Tokyo mua_CM      GMT+09:00
Asia/Tokyo my          ဂျပန် စံတော်ချိန်
Asia/Tokyo my_MM       ဂျပန် စံတော်ချိန်
Asia/Tokyo naq         GMT+09:00
Asia/Tokyo naq_NA      GMT+09:00
Asia/Tokyo nb          japansk normaltid
Asia/Tokyo nb_NO       japansk normaltid
Asia/Tokyo nb_SJ       japansk normaltid
Asia/Tokyo nd          GMT+09:00
Asia/Tokyo nd_ZW       GMT+09:00
Asia/Tokyo ne          जापान मानक समय
Asia/Tokyo ne_IN       जापान मानक समय
Asia/Tokyo ne_NP       जापान मानक समय
Asia/Tokyo nl          Japanse standaardtijd
Asia/Tokyo nl_AW       Japanse standaardtijd
Asia/Tokyo nl_BE       Japanse standaardtijd
Asia/Tokyo nl_BQ       Japanse standaardtijd
Asia/Tokyo nl_CW       Japanse standaardtijd
Asia/Tokyo nl_NL       Japanse standaardtijd
Asia/Tokyo nl_SR       Japanse standaardtijd
Asia/Tokyo nl_SX       Japanse standaardtijd
Asia/Tokyo nmg         GMT+09:00
Asia/Tokyo nmg_CM      GMT+09:00
Asia/Tokyo nn          GMT+09:00
Asia/Tokyo nn_NO       GMT+09:00
Asia/Tokyo nnh         GMT+09:00
Asia/Tokyo nnh_CM      GMT+09:00
Asia/Tokyo nus         GMT+09:00
Asia/Tokyo nus_SD      GMT+09:00
Asia/Tokyo nyn         GMT+09:00
Asia/Tokyo nyn_UG      GMT+09:00
Asia/Tokyo om          GMT+09:00
Asia/Tokyo om_ET       GMT+09:00
Asia/Tokyo om_KE       GMT+09:00
Asia/Tokyo or          GMT+09:00
Asia/Tokyo or_IN       GMT+09:00
Asia/Tokyo os          GMT+09:00
Asia/Tokyo os_GE       GMT+09:00
Asia/Tokyo os_RU       GMT+09:00
Asia/Tokyo pa          ਜਪਾਨ ਮਿਆਰੀ ਸਮਾਂ
Asia/Tokyo pa_Arab     GMT+۰۹:۰۰
Asia/Tokyo pa_Arab_PK  GMT+۰۹:۰۰
Asia/Tokyo pa_Guru     ਜਪਾਨ ਮਿਆਰੀ ਸਮਾਂ
Asia/Tokyo pa_Guru_IN  ਜਪਾਨ ਮਿਆਰੀ ਸਮਾਂ
Asia/Tokyo pl          Japonia (czas standardowy)
Asia/Tokyo pl_PL       Japonia (czas standardowy)
Asia/Tokyo ps          GMT+۰۹:۰۰
Asia/Tokyo ps_AF       GMT+۰۹:۰۰
Asia/Tokyo pt          Horário Padrão do Japão
Asia/Tokyo pt_AO       Hora Padrão do Japão
Asia/Tokyo pt_BR       Horário Padrão do Japão
Asia/Tokyo pt_CV       Hora Padrão do Japão
Asia/Tokyo pt_GW       Hora Padrão do Japão
Asia/Tokyo pt_MO       Hora Padrão do Japão
Asia/Tokyo pt_MZ       Hora Padrão do Japão
Asia/Tokyo pt_PT       Hora Padrão do Japão
Asia/Tokyo pt_ST       Hora Padrão do Japão
Asia/Tokyo pt_TL       Hora Padrão do Japão
Asia/Tokyo qu          GMT+09:00
Asia/Tokyo qu_BO       GMT+09:00
Asia/Tokyo qu_EC       GMT+09:00
Asia/Tokyo qu_PE       GMT+09:00
Asia/Tokyo rm          GMT+09:00
Asia/Tokyo rm_CH       GMT+09:00
Asia/Tokyo rn          GMT+09:00
Asia/Tokyo rn_BI       GMT+09:00
Asia/Tokyo ro          Ora standard a Japoniei
Asia/Tokyo ro_MD       Ora standard a Japoniei
Asia/Tokyo ro_RO       Ora standard a Japoniei
Asia/Tokyo rof         GMT+09:00
Asia/Tokyo rof_TZ      GMT+09:00
Asia/Tokyo ru          Япония, стандартное время
Asia/Tokyo ru_BY       Япония, стандартное время
Asia/Tokyo ru_KG       Япония, стандартное время
Asia/Tokyo ru_KZ       Япония, стандартное время
Asia/Tokyo ru_MD       Япония, стандартное время
Asia/Tokyo ru_RU       Япония, стандартное время
Asia/Tokyo ru_UA       Япония, стандартное время
Asia/Tokyo rw          GMT+09:00
Asia/Tokyo rw_RW       GMT+09:00
Asia/Tokyo rwk         GMT+09:00
Asia/Tokyo rwk_TZ      GMT+09:00
Asia/Tokyo sah         GMT+09:00
Asia/Tokyo sah_RU      GMT+09:00
Asia/Tokyo saq         GMT+09:00
Asia/Tokyo saq_KE      GMT+09:00
Asia/Tokyo sbp         GMT+09:00
Asia/Tokyo sbp_TZ      GMT+09:00
Asia/Tokyo se          UTC+09:00
Asia/Tokyo se_FI       UTC+09:00
Asia/Tokyo se_NO       UTC+09:00
Asia/Tokyo se_SE       UTC+09:00
Asia/Tokyo seh         GMT+09:00
Asia/Tokyo seh_MZ      GMT+09:00
Asia/Tokyo ses         GMT+09:00
Asia/Tokyo ses_ML      GMT+09:00
Asia/Tokyo sg          GMT+09:00
Asia/Tokyo sg_CF       GMT+09:00
Asia/Tokyo shi         GMT+09:00
Asia/Tokyo shi_Latn    GMT+09:00
Asia/Tokyo shi_Latn_MA GMT+09:00
Asia/Tokyo shi_Tfng    GMT+09:00
Asia/Tokyo shi_Tfng_MA GMT+09:00
Asia/Tokyo si          ජපාන සම්මත වේලාව
Asia/Tokyo si_LK       ජපාන සම්මත වේලාව
Asia/Tokyo sk          Japonský štandardný čas
Asia/Tokyo sk_SK       Japonský štandardný čas
Asia/Tokyo sl          Japonski standardni čas
Asia/Tokyo sl_SI       Japonski standardni čas
Asia/Tokyo smn         GMT+09:00
Asia/Tokyo smn_FI      GMT+09:00
Asia/Tokyo sn          GMT+09:00
Asia/Tokyo sn_ZW       GMT+09:00
Asia/Tokyo so          GMT+09:00
Asia/Tokyo so_DJ       GMT+09:00
Asia/Tokyo so_ET       GMT+09:00
Asia/Tokyo so_KE       GMT+09:00
Asia/Tokyo so_SO       GMT+09:00
Asia/Tokyo sq          Ora standarde e Japonisë
Asia/Tokyo sq_AL       Ora standarde e Japonisë
Asia/Tokyo sq_MK       Ora standarde e Japonisë
Asia/Tokyo sq_XK       Ora standarde e Japonisë
Asia/Tokyo sr          Јапанско стандардно време
Asia/Tokyo sr_Cyrl     Јапанско стандардно време
Asia/Tokyo sr_Cyrl_BA  Јапанско стандардно време
Asia/Tokyo sr_Cyrl_ME  Јапанско стандардно време
Asia/Tokyo sr_Cyrl_RS  Јапанско стандардно време
Asia/Tokyo sr_Cyrl_XK  Јапанско стандардно време
Asia/Tokyo sr_Latn     Japansko standardno vreme
Asia/Tokyo sr_Latn_BA  Japansko standardno vreme
Asia/Tokyo sr_Latn_ME  Japansko standardno vreme
Asia/Tokyo sr_Latn_RS  Japansko standardno vreme
Asia/Tokyo sr_Latn_XK  Japansko standardno vreme
Asia/Tokyo sv          japansk normaltid
Asia/Tokyo sv_AX       japansk normaltid
Asia/Tokyo sv_FI       japansk normaltid
Asia/Tokyo sv_SE       japansk normaltid
Asia/Tokyo sw          Saa Wastani za Japani
Asia/Tokyo sw_KE       Saa Wastani za Japani
Asia/Tokyo sw_TZ       Saa Wastani za Japani
Asia/Tokyo sw_UG       Saa Wastani za Japani
Asia/Tokyo swc         GMT+09:00
Asia/Tokyo swc_CD      GMT+09:00
Asia/Tokyo ta          ஜப்பான் நிலையான நேரம்
Asia/Tokyo ta_IN       ஜப்பான் நிலையான நேரம்
Asia/Tokyo ta_LK       ஜப்பான் நிலையான நேரம்
Asia/Tokyo ta_MY       ஜப்பான் நிலையான நேரம்
Asia/Tokyo ta_SG       ஜப்பான் நிலையான நேரம்
Asia/Tokyo te          జపాన్ ప్రామాణిక సమయం
Asia/Tokyo te_IN       జపాన్ ప్రామాణిక సమయం
Asia/Tokyo teo         GMT+09:00
Asia/Tokyo teo_KE      GMT+09:00
Asia/Tokyo teo_UG      GMT+09:00
Asia/Tokyo th          เวลามาตรฐานญี่ปุ่น
Asia/Tokyo th_TH       เวลามาตรฐานญี่ปุ่น
Asia/Tokyo ti          GMT+09:00
Asia/Tokyo ti_ER       GMT+09:00
Asia/Tokyo ti_ET       GMT+09:00
Asia/Tokyo to          houa fakasiapani taimi totonu
Asia/Tokyo to_TO       houa fakasiapani taimi totonu
Asia/Tokyo tr          Japonya Standart Saati
Asia/Tokyo tr_CY       Japonya Standart Saati
Asia/Tokyo tr_TR       Japonya Standart Saati
Asia/Tokyo twq         GMT+09:00
Asia/Tokyo twq_NE      GMT+09:00
Asia/Tokyo tzm         GMT+09:00
Asia/Tokyo tzm_Latn    GMT+09:00
Asia/Tokyo tzm_Latn_MA GMT+09:00
Asia/Tokyo ug          ياپونىيە ئۆلچەملىك ۋاقتى
Asia/Tokyo ug_Arab     ياپونىيە ئۆلچەملىك ۋاقتى
Asia/Tokyo ug_Arab_CN  ياپونىيە ئۆلچەملىك ۋاقتى
Asia/Tokyo uk          за японським стандартним часом
Asia/Tokyo uk_UA       за японським стандартним часом
Asia/Tokyo ur          جاپان سٹینڈرڈ ٹائم
Asia/Tokyo ur_IN       جاپان سٹینڈرڈ ٹائم
Asia/Tokyo ur_PK       جاپان سٹینڈرڈ ٹائم
Asia/Tokyo uz          Yaponiya standart vaqti
Asia/Tokyo uz_Arab     GMT+۰۹:۰۰
Asia/Tokyo uz_Arab_AF  GMT+۰۹:۰۰
Asia/Tokyo uz_Cyrl     Япония стандарт вақти
Asia/Tokyo uz_Cyrl_UZ  Япония стандарт вақти
Asia/Tokyo uz_Latn     Yaponiya standart vaqti
Asia/Tokyo uz_Latn_UZ  Yaponiya standart vaqti
Asia/Tokyo vai         GMT+09:00
Asia/Tokyo vai_Latn    GMT+09:00
Asia/Tokyo vai_Latn_LR GMT+09:00
Asia/Tokyo vai_Vaii    GMT+09:00
Asia/Tokyo vai_Vaii_LR GMT+09:00
Asia/Tokyo vi          Giờ Chuẩn Nhật Bản
Asia/Tokyo vi_VN       Giờ Chuẩn Nhật Bản
Asia/Tokyo vun         GMT+09:00
Asia/Tokyo vun_TZ      GMT+09:00
Asia/Tokyo wae         GMT+09:00
Asia/Tokyo wae_CH      GMT+09:00
Asia/Tokyo xog         GMT+09:00
Asia/Tokyo xog_UG      GMT+09:00
Asia/Tokyo yav         GMT+09:00
Asia/Tokyo yav_CM      GMT+09:00
Asia/Tokyo yi          GMT+09:00
Asia/Tokyo yi_001      GMT+09:00
Asia/Tokyo yo          GMT+09:00
Asia/Tokyo yo_BJ       GMT+09:00
Asia/Tokyo yo_NG       GMT+09:00
Asia/Tokyo zgh         GMT+09:00
Asia/Tokyo zgh_MA      GMT+09:00
Asia/Tokyo zh          日本标准时间
Asia/Tokyo zh_Hans     日本标准时间
Asia/Tokyo zh_Hans_CN  日本标准时间
Asia/Tokyo zh_Hans_HK  日本标准时间
Asia/Tokyo zh_Hans_MO  日本标准时间
Asia/Tokyo zh_Hans_SG  日本标准时间
Asia/Tokyo zh_Hant     日本標準時間
Asia/Tokyo zh_Hant_HK  日本標準時間
Asia/Tokyo zh_Hant_MO  日本標準時間
Asia/Tokyo zh_Hant_TW  日本標準時間
Asia/Tokyo zu          Isikhathi esivamile sase-Japan
Asia/Tokyo zu_ZA       Isikhathi esivamile sase-Japan

文字列の抽出

文字列のコピーでコピーする文字数を限定する関数***ncpy系の関数がありましたので、前方抽出はできまるようになったと思います。他にもtokenを使ったり、正規表現を使う事でも抽出ができました。これらの文字の位置を示す関数と、この他にも存在する文字の位置に関する関数を活用することで、もっと他の抽出方法を考えることもできます。前方から何文字目~n文字。前方のnバイト目に目的の文字開始位置があれば、そこからn文字、前方のnバイト目に目的の文字開始位置があって、次のmバイト目に終端位置があって、その間にあるn文字を抽出といった具合です。これらはマルチバイト文字がおさめられた文字列変数ではバイト数と文字数が一致しないことから慎重に処理する必要があります。またワイド文字も2バイト固定のように感じてしまいますが、実際は4バイトの文字である可能性も0ではないので、きちんと文字数とバイト数を計算して、開始位置に文字型ポインターを格納したりする必要があります。配列によって決まった長さの実体のある文字列で終端に\0が入っているような構造の先頭を指し示すポインタと、そのポインタの型と同じだけど実体は持たないポインタの変数を利用して、実体を持たないポインタ変数が、文字の抽出開始位置を指し示すようにすること、そこらからコピーする文字数を正確に数えることがポイントになります。ポイントというとポインタと紛らわしいので、ミソになりますとでも言った方がいいのかもしれません。ここで紹介する抽出と、これまでに紹介した正規表現やtokenを活用すると更に操作の幅が広がります。


とにかく文字を数えて、文字列ポインタを移動させ、サロゲートペアなら4バイト、通常のワイド文字範囲なら2byteってなかんじで数えます。面倒ですが、サロゲートペアのことを考えると文字数での抽出はこのような気遣いが必要です。マルチバイト文字でやるときは、また違った対応が必要になります。仕組みとしては文字数とバイト数をきちんと数えて、先頭文字にポインタを合わせる。***ncpy系の関数でそこから必要なバイト数をコピーするという感じになります。コンソール画面ではShiftJISなので文字化けしますから、Unicode文字としてテキストに出力するプログラムになっています。今回のプログラムでは対応しませんでしたが、異体字セレクタに対応すると、2バイト文字にはさらに DB40 DD00 の4バイトが付与されていますので、次の4バイトの値がDB40 DD**になっていないか確認をする必要があり、そのようになっている場合は+4バイトするような計算も必要です。テキストエディタを作成する場合にはフォントを異体字セレクタにしたがって切り替える試みが必要になります。フォントが対応していなければ切り替えることはできないので、処理としては元の文字を表示するだけになります。DB40 DD** のような異体字補助番号は文字コードとして使われないことになっています。

#pragma once
#include "stdafx.h"
#include <stdlib.h>
#include <string>      //std::string型定義
#include <mbstring.h>	 //mbs***関数
#include <iostream>    //cpp cout etc 一般入出力関数
#include <locale>      //Locale関数
#include <tchar.h>     //TCHAR型+_tcs***関数
#include "atlstr.h"    //CString,CStringA,CStringWおよびLPTSTR,LPWSTR,LPSTR,LPCTSTR,LPCWSTR,LPCSTRの定義プリプロセッサ
#include "atlbase.h"   //同上
#include "cstringt.h"  //CStringTの定義プリプロセッサ
#include <vector>      //Vector型テンプレートクラス

#include <sys/timeb.h> //Timeb型構造体

//_bstr_t型を利用するプリプロセッサ(ライブラリはデバッグモードとリリースモード個別設定)
#include "comutil.h"

#ifdef _DEBUG
#pragma comment(lib, "comsuppwd.lib")
#else
#pragma comment(lib, "comsuppw.lib")
#endif
//_bstr_t _End

//ICU ucnvプリプロセッサ(ライブラリはデバッグモードとリリースモード個別設定)
#include <unicode/ucnv.h>     //ucnv文字コード変換ライブラリヘッダ
#include <unicode/translit.h> //文字変換ライブラリヘッダ
#include <unicode/regex.h>    //正規表現
#include <unicode/ucsdet.h>   //文字コード判定
#include <unicode/ucal.h>   //文字コード判定
#include <unicode/uclean.h> //u_cleanup関数
/* for uloc_getDefault() */
#include <unicode/uloc.h>
#include <unicode/calendar.h>
#include <unicode/smpdtfmt.h>

//#include "unicode/utypes.h"
//#include "unicode/locid.h"
//#include "unicode/unistr.h"
//#include "unicode/tzfmt.h"
//#include "unicode/tznames.h"


#ifdef _DEBUG
#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuucd.lib")  //ICU ucnvを使うために必要なライブラリ
#pragma comment(lib, "icuind.lib")  //ICU Transliterate関数を使うために必要なライブラリ 正規表現関数も
#pragma comment(lib, "iculed.lib")
#pragma comment(lib, "iculxd.lib")  
#pragma comment(lib, "icuiod.lib")  
#pragma comment(lib, "icutud.lib")  

#else
//#pragma comment(lib, "icudt.lib")
#pragma comment(lib, "icuind.lib")  //ICU Transliterate関数を使うために必要なライブラリ 正規表現関数も
#pragma comment(lib, "icuuc.lib")   //ICU ucnvを使うために必要なライブラリ
//#pragma comment(lib, "icuio.lib")
#endif
//Data   Library                     icudtXX(d).dll icudt(d).lib
//Common Library                     icuucXX(d).dll icuuc(d).lib
//Internationalization(i18n) Library icuinXX(d).dll icuin(d).lib
//Layout Engine                      iculeXX(d).dll icule(d).lib
//Layout Extention Engine            iculxXX(d).dll iculx(d).lib
//ICU I/O(Unicode stdio) Library     icuioXX(d).dll icuio(d).lib
//Tool Utility Library               icutuXX(d).dll icutu(d).lib

//ICU ucnv _End

#include "UnicodeConverter.h"

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
  _tsetlocale(LC_ALL, _T("Japanese")); //ロケールセットすると_l関数でロケール指定しなくても日本語が使われます。

	_locale_t localeJpanease;
	localeJpanease = _create_locale(LC_ALL, "Japanese");

  FILE* pfileText;
  fpos_t* fposPos = new fpos_t;
  errno_t errNo;
  size_t* psizeRuturnValue = new size_t;
  
  wchar_t pwcStrFilePath[]             = L"C:\\...\\...\\...\\test_new_Extract.txt";
  char* pcStrStream;
  pcStrStream = new char[1024];
  int nCloseResult = 0;
  
  wchar_t pwcStrSurrogate[] = L"\xD840\xDC0B\xD867\xDE3Dごはん\xD840\xDC0B\xD842\xDFB7を食べたYo。";//L"𠀋𩸽ごはん𠀋𠮷を食べたYo。"と同じ。
  const wchar_t* pwcStrPos;//実体は持たない文字位置格納変数
  const wchar_t* pwcStrStPos;//実体は持たない文字位置格納変数
  wchar_t* pwcStrSurrogateExtract;

  pwcStrPos = pwcStrSurrogate;
  int nSize =  wcslen(pwcStrSurrogate);
  int nByteCnt = 0;
  int nCharCnt = 0;
  int nStPos = 2;//文字位置では0文字目からカウントする仕組みなので、3文字目にポインタが合います。
  printf("%d\n",nSize);
  while (nByteCnt < nSize && nCharCnt < nStPos){
    unsigned int unValue = _wcsnextc(pwcStrPos);
    pwcStrPos++;
    printf("%0x:",unValue);
    if(unValue >= 0xD800 && unValue <= 0xDFFF){  
      pwcStrPos++;
      nByteCnt++;
    }
    nCharCnt++;
    nByteCnt++;
  }
  pwcStrStPos = pwcStrPos;//先頭の文字列にポインタを合わせる。
  printf("\n");
  printf("%d[Byte],%d[文字]\n",nByteCnt,nCharCnt);
  int nEnPos = 5;//先頭文字から5文字目までのバイト数を数えます。そういう意味では最初の先頭文字の指定と合わせた方が良かったかも。
  nByteCnt = 0;
  nCharCnt = 0;
  nSize =  wcslen(pwcStrPos);

  printf("%d\n",nSize);
  while (nByteCnt < nSize && nCharCnt < nEnPos){
    unsigned int unValue = _wcsnextc(pwcStrPos);
    pwcStrPos++;
    printf("%0x:",unValue);
    if(unValue >= 0xD800 && unValue <= 0xDFFF){  
      pwcStrPos++;
      nByteCnt++;
    }
    nCharCnt++;
    nByteCnt++;
  }
  printf("\n");

  pwcStrSurrogateExtract = new wchar_t[nByteCnt + 1];
  wcsncpy_s(pwcStrSurrogateExtract, nByteCnt + 1, pwcStrStPos, nByteCnt);//5文字分のバイト数をコピーする。7バイトだね。

  printf("%d[Byte],%d[文字]\n",nByteCnt,nCharCnt);
  wprintf(L"%s",pwcStrPos);
  printf("\n");

  fpos_t* pfposStartPos = new fpos_t;
  printf("★テキストの新規書き込み\n");
  errNo = _wfopen_s( &pfileText, pwcStrFilePath, L"w,ccs=UTF-16LE");
  printf("Error?->%d\n", errNo);
  
  fputws(pwcStrSurrogate,pfileText);
  fputws(L"\n",pfileText);

  fputws(pwcStrStPos,pfileText);
  fputws(L"\n",pfileText);

  fputws(pwcStrPos,pfileText);
  fputws(L"\n",pfileText);

  fputws(pwcStrSurrogateExtract,pfileText);

  fclose(pfileText);
  return 0;
}

コンソール側出力

18
d840:d842:
4[Byte],2[文字]
14
3054:306f:3093:d840:d842:
7[Byte],5[文字]
を食べたYo。
★テキストの新規書き込み
Error?->0

テキスト出力

𠀋𩸽ごはん𠀋𠮷を食べたYo。
ごはん𠀋𠮷を食べたYo。
を食べたYo。
ごはん𠀋𠮷


という感じで、以上ですかね。


基本機能を用いて、文字列操作のすべてを掌握できるような、異常なまでに長い記事を書いたつもりです。それくらい文字列操作はコンピュータを使う人から喜ばれるプログラミングだと思います。文字を扱うプログラムは意思を疎通したり記録したり、人間文化の中枢にある処理です。プログラムによって文字情報をうまく表示してくれたり、編集してくれたり、閲覧できるようになることは、ありがたいこと、この上ない機能でしょう。ここで紹介したものだけでは文字処理の効率はイマイチかもしれません。便利な文字列クラスはこのWebの世界をたぐり探せば存在するかもしれません。ですが、C++範疇の文字列操作のエッセンスとしては、ここで紹介したものがほとんど全てとなります。この長い記事を参考に理解する努力をしてみたのなら、やりたい文字列操作をどういう具合にやっていけばいいのかという基本というかコツみたいなものはつかめたと思います。つかめなかった人もヒントくらいにはなったのではないでしょうか?ここから先はプログラムされる人のそれぞれの文字列操作感覚に従って、プログラムを作りこんでいっていただければと思います。基本がわかっていれば、Webで検索してでてくるようなややこしい説明も理解していけるでしょう。やりたいことを実現するのは面倒で手数も多いと実感したと思います。効率よくやりたいことができるようになるまで、知恵を振り絞って、頑張ってみてください。ここまで理解しようとしたならきっとできるはずです。


ここを参照してくれたみなさんが作れってくれたアプリが使いやすいものになって、いろんな人の人生を充実させるものになることを願います。


C++へ戻る