C 文字列操作のソースを表示
新しいページはコチラ
移動:
案内
,
検索
※このページではC言語にも存在していたという意味で記事タイトルがC 文字列操作になっていますが、<br /> [[C PlusPlus|C++]]でも同様です。[[C PlusPlus|C++]]だけの機能がある場合は明記します。<br /> <br /> == '''文字列操作''' == 文字列のコピー、連結、比較、一致、区切り文字分割、探索、文字列長取得といった操作を文字列操作と呼びます。上記のような操作はCRT関数として提供されているため、関数の使い方さえ覚えれば簡単に操作できます。 更に、文字列の型変換、文字コード変換、大文字小文字変換、半角文字、全角文字変換、ファイルパス操作、ファイル名操作、拡張子取得といった文字列操作の応用もプログラミングでは必要になってきますし、文字列の検索と置換、ファイルへの入出力といった操作までを習得すると概ねの操作ができるようになります。<br /> 上記のように日本語でまとめて言い換えるのは簡単ですが、一つ一つを実際にやってみるとなると、そう簡単でもありません。メモリに保管される文字列を操作するというのは、手続きが複雑です。コピーひとつにしても、どういった文字列を基にして、何文字目から何文字目までをコピーするのか?それにはどれくらいの大きさの文字列長があるのか?どういった変数に格納するのか?領域はちゃんと確保されているのか?手続きがうまくいかなかった場合はどうするのか?と何気に細かいことを教えてあげないと動かないのがコンピュータです。コンピュータ側からすると、人間のようには解釈は出来ない、そのかわりわからないことがあれば、どんな地道なことをしててでも調べてやるから、コンピュータに指令するのに必要な情報をそろえてほしい。<br /> まとめては動作させれないけど、内部的にはひとつづつ着実にコピーして、最終的には全部をコピーしてくれる。なんだか新入社員のようにいちいち指示しないといけないけど、まぁ的確に指示をすればやる気だけはあって、こなしてくれる。新入社員はほっといても成長しますが、コンピュータはほっとくと延々と成長しません。新入社員さんでも、あまりに放置すると教育不足ということで、指導者の責任問題とか、新人さんの人生を曲げてしまうという強烈な負のスパイラルが発生します。人間にはコンピュータにはまだまだ備えることのできない心って奴があります。大事にしないとね。ともかく、コンピュータが勝手にやってくれるようになるには、勝手にやれるようになるための成長するプログラムが必要になります。偉い人たちは地道にやっているようです。<br /> 成長云々の件はここでは関係ありませんので、置いておくとして、文字列の操作をひとつづつやってみましょう。(書きかけの記事です。最終更新131128)<br /> 文字列操作に関する関数は以下のアドレスのとおりです。<br /> http://msdn.microsoft.com/ja-jp/library/f0151s4x.aspx<br /> 上記があれば、自分が説明する必要はもうないかなと思うわけですが、一応、自分なりに。<br /> =='''文字列長取得'''== 文字列の長さを知りたいと思うことは、日常ではほとんどないですが、プログラムでは必要になるパターンが多いです。例えば、どれくらいのメモリサイズを確保すれば、いいのか?とか、文字列から抜き出した文字数が長い場合にはスクロールバーをつけたりとか、文字列の長さによって処理を分岐させたりすることもあります。この他、C言語においては、ほとんどの文字列操作関数で、その操作する文字列変数の文字列の長さを引数として与えなければならなかったりと、文字列操作の中でも基本中の基本となる部分です。知能指数の低い自分は文字数の足し算、引き算で間違えたりすることも多いです。うまく数えないと、枠にぴったりはまらないのがプログラム。<br /> リファレンスは以下のとおりです。<br /> http://msdn.microsoft.com/ja-jp/library/78zh94ax.aspx<br /> http://msdn.microsoft.com/ja-jp/library/z50ty2zh.aspx<br /> _tcslen(TCHA型設定時)/wcslen(Unicode設定)/strlen(マルチバイト設定)マルチバイト設定時の日本語対応関数 _mbslenになります。第一引数がNULLのとき、アクセス違反が発生します。<br /> lenの前にnが付くものは第二引数に最大文字数を設定する必要があり、文字列がそれを超えた場合は最大文字数を返します。<br /> _tcsnlen(TCHA型設定時)/wcsnlen(Unicode設定)/strnlen(マルチバイト設定) サフィックスに_sが付く関数では、第一引数がNULLときは戻り値が0になります。<br /> マルチバイト設定時の日本語対応関数 _mbsnlen<br /> <br /> _mbstrlenおよび_mbstrnlen は無効なマルチバイトを含む場合に戻り値-1を返します。_mbstrnlen は最大文字を超えると例外処理が発生し戻り値に-1とするとともにEINVAL に errnoを格納します。<br /> 更にサフィックスに_lが付くものは最後の引数として、ロケール設定ができます。ロケール設定の無い関数はsetlocale関数で設定された値に従います。<br /> 全部で<br /> 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 <br /> これだけの種類があります。<br /> 要するに?<br /> _tcslenを使うのが良いでしょう。Locale設定はあらかじめ実施しておくのが良いと思います。strnlen_sやwcsnlen_sはNULLの文字列の場合に0を返してくれるのはなんか良さげですが、文字列の終端が必ず\0で終わっていさえすれば必要のないことです。それはヌル文字でもいいわけです。アドレスがヌルのヌルポインターのときにプログラムが止まるよって言ってます。なんか、いっぱいありすぎて迷うけど…。 _tcslen!<br /> _lが付く関数は個別にLocale変換が必要な場合にのみ使えば良いでしょう。_tcsnlenで最大文字数を設定するのは、一見安全そうですが、なんか無駄なような気がします。あきらかに最大文字数がわかるような場合には、なんか入れておいて_tcsnlen_sってのはいい選択肢だと思います。<br /> <syntaxhighlight lang="cpp" line start="1"> #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])); } } </syntaxhighlight> 出力結果 <syntaxhighlight lang="cpp"> 0/ 3:cStr0[ 0]=表示:よねウィキの機能<yonewiki> StrCount=21 1/ 3:cStr0[ 1]=表示:よねウィキの機能1<yonewiki> StrCount=22 2/ 3:cStr0[ 2]=表示:よねウィキの機能2<yonewiki> StrCount=22 </syntaxhighlight> _mb系の関数を利用するには、<nowiki>#include <mbstring.h></nowiki>をインクルードする必要があります。<br /> 更に_mbslenは文字列長探索をしたい文字列変数の引数が、unsigned char型ですので、wchar_t型、char型の引数では文字列長を探索することは出来ません。あえて実施するならば、reinterpret_cast<unsigned char*>(※charポインタ型※)と強制的なキャストを実施することで、動作させることができます。_mbstrlenはchar型を引数にしますので、wchar_t型を使わない場合の日本語文字列長検索として使うことが出来ます。<br /> 但し、この強制キャストは定数文字列const宣言のある文字列に対しては操作が出来ないため、文字列のコピーを作成する必要があります。文字列コピー操作の詳細は次の項目に記載予定なので、順番に読み進めている人は、コピー操作について理解してから戻ってきた方が良いかもしれません。<br /> <syntaxhighlight lang="cpp" line start="1"> #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; } </syntaxhighlight> という具合にして、const char→char→_mabslenで文字列長探索が出来ます。出力結果は以下のとおりです。<br /> <syntaxhighlight lang="cpp"> 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 </syntaxhighlight> _mbstrlen関数を使う場合はコピー操作が必要にならず、const宣言している文字列変数を使っての文字列長探索ができます。<br /> <syntaxhighlight lang="cpp" line start="1"> #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])); } } </syntaxhighlight> 出力結果は以下のとおりです。<br /> <syntaxhighlight lang="cpp"> 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 </syntaxhighlight> といった具合にいろいろな関数があるので、一つづつ掘り下げて理解しておくのも、必要です。こういう違いがあるということを把握していれば、臨機応変に対応しやすくなるかもです。自分は分かったつもりにならずに、常に謙虚に確かめてみる姿勢ってのは、必要なんじゃないかと思います。そして常に、自分が実施するであろうパターンを網羅して確かめる。だから、あえて文字列の配列の場合にどうやればいいのかを探ります。 =='''文字列コピー'''== 文字列のコピー?なんでコピーするの?ってなりそうですけど、変数の代入みたいなものです。元の文字列はとっておいて、コピーした方を加工するということです。あるいはその逆。紙のコピーだと原版が綺麗だけど、原版もコピーも同じだから…<br /> a=13;<br /> b=a;<br /> b=b*b;<br /> b=a;<br /> b=b*b*b;<br /> ってな感じのことやったりしません?aの値は取っておきたい。そういうときに使ったりするのかなぁと思います。<br /> リファレンスは以下のとおりです。<br /> http://msdn.microsoft.com/ja-jp/library/kk6xf663.aspx<br /> http://msdn.microsoft.com/ja-jp/library/td1esda9.aspx<br /> http://msdn.microsoft.com/ja-jp/library/xdsywd25.aspx<br /> http://msdn.microsoft.com/ja-jp/library/5dae5d43.aspx<br /> また、例によってマルチバイト文字版strcpy、ワイド文字版wcscpy、マルチバイト2バイト文字認識版_mbscpy、セキュリティ強化版の_sサフィックスがあります。_s無し版の引数はコピー先文字列変数先頭char型アドレス、コピー元文字列変数先頭char型アドレス(文字列の終端に\0があること)となっています。_mbscpyの引数は第一引数、第二引数共にunsigned charになっています。この微妙に使いにくい引数の異なり…。しょうがないのかなぁ。<br /> そして、_s版は第2引数にコピー先文字列変数先頭char型アドレスの大きさを必要とします。_sが付かない関数で第二引数だったコピー元は第三引数にずれます。strncpyのようにcpyの前にnが付くパターンでマルチバイト文字版、ワイド文字版、マルチ2バイト文字対応版と更には_lサフィックスでロケール個別設定ができる関数があります。マルチバイト2バイト文字対応版にはバイト長を引数とするcpyの前にbが付いたものもあります。> でも…マルチバイト2バイト文字対応文字ってもともと配列の要素一つに1バイトを格納しているから、バイト長で扱っても、要素数で扱っても変わらないから何が違うのか?よくわからない。_mbs系のcpy関数の利点ってなんだろう。このへんはまた今度、考えてみよう。 全部で、<br /> strcpy、wcscpy、_mbscpy , strcpy_s、wcscpy_s、_mbscpy_s ,<br /> strncpy、_strncpy_l、wcsncpy、_wcsncpy_l、_mbsncpy、_mbsncpy_l , strncpy_s、_strncpy_s_l、wcsncpy_s、_wcsncpy_s_l、_mbsncpy_s、_mbsncpy_s_l<br /> _mbsnbcpy、_mbsnbcpy_l , _mbsnbcpy_s、_mbsnbcpy_s_l <br /> これだけあります。<br /> で、結局は_tcsncpy_sを使いなさいってことになります。Unicode設定ならワイド文字版で、文字列長に厳しい設定が必要なwcsncpy_sだね。第二引数は配列の大きさ+終端\0のための要素1つ分。バイト数ではないです。動的に生成した変数の場合は配列の大きさが取得できないので、wcslen(_tcslen)のような文字列長取得関数を使って、戻ってきた値に\0の要素のために1を加算した値が良いです。<br /> <syntaxhighlight lang="cpp" line start="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; } </syntaxhighlight> という感じでしたね。無理やり_mbscpy関数を使ってみるとこんな感じですね。unsigned char型は宣言と同時にnew演算子の定義は出来ないようです。const型のcharからはキャストできないから、結局は一度、strcpy_sを使って、char型の文字列に置き換えないといけないです。マルチバイト型の2バイト文字認識操作関数って結局は、char型と同じなので、文字数を数えるときはstrlenで数えないといけない。char型と_mbsxxx関数を行ったり来たりするだけです。<br /> 出力結果は以下のとおり<br /> <syntaxhighlight lang="cpp"> _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>, </syntaxhighlight> 上記に加えて、コピーする文字数を引数とするcpyの前にnが付く関数_tcsncpy_s/wcsncpy_s/strncpy_s/_mbsncpy_sにする場合は<br /> <syntaxhighlight lang="cpp"> … … //_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])));//ココ! … … </syntaxhighlight> と、4つの引数をとるように記述します。第4引数が出力する最大文字の配列数です。出力する文字数と考えることもできます。マルチバイト文字の場合には出力する文字数を指定できるのは効果的で、先頭から何バイト目で区切れば日本語文字が分断されないかの判断もしてくれながらの出力となります。この出力する文字数をあえてバイト単位で指定する_mbsnbcpy_sもあります。ただし、コピー先の文字列の配列はstrlenのようなバイト数分で準備する必要があることに注意が必要です。出力文字数を指定する場合は文字列全体の長さではなく、指定した文字数で必要な文字列バイト数を算出しておいて、メモリを確保するように処理を記述するのが良いかもしれません。ここでは強制キャストを使いましたが、もともとの文字列がunsigned charとして定義されているものをコピーするときに_mbs系の文字列コピーを利用するというのが自然な使い方になります。<br /> =='''文字列連結'''== 文字列の処理において、文字列を抜き出すことと、連結することはプログラミングにおいて、かなり頻繁に必要となる処理です。設定ファイルのようなものを書き出す場合に、設定1の値をAとユーザが指定した場合、文字列リテラルとして、「<設定1="」と「A」と「">」とを繋ぎ合わせて、記憶しておいて、あとで書き出し処理をしたり、あとで設定ファイルの中身を見る時に設定1の値を抜き出すためにAという値を抜き出す。そういう感じです。上記のようなXML形式の設定方法ならパースという手法によって、既に設定属性値と設定値を書き込んだり、読み込んだりするための関数が提供されていたりしますが、同じようなことを自分でやっていくということはあります。<br /> 連結は英語でcatenateと言うため、strcatのようなcatというキーワードが関数に使われます。<br /> リファレンスは以下の通りです。<br /> http://msdn.microsoft.com/ja-jp/library/tbyd7s1y.aspx<br /> http://msdn.microsoft.com/ja-jp/library/w6w3kbaf.aspx<br /> http://msdn.microsoft.com/ja-jp/library/td1esda9.aspx<br /> http://msdn.microsoft.com/ja-jp/library/d45bbxx4.aspx<br /> http://msdn.microsoft.com/ja-jp/library/5c6eeh98.aspx<br /> http://msdn.microsoft.com/ja-jp/library/h1x0y282.aspx<br /> 多い…<br /> strncat、_strncat_l、wcsncat、wcsncat_l、_mbsncat、_mbsncat_l , strncat_s、_strncat_s_l、wcsncat_s、_wcsncat_s_l、_mbsncat_s、_mbsncat_s_l <br /> strcat、wcscat、_mbscat , strcat_s、wcscat_s、_mbscat_s <br /> _mbsnbcat、_mbsnbcat_l , _mbsnbcat_s、_mbsnbcat_s_l<br /> <br /> 例によって_sのついた連結先文字列の配列サイズを明記する関数に_lのついた個別ロケール設定関数。そして連結させたい文字列の文字数を記述するncatに文字数をバイト数で指定するnbcat。あとはワイド文字のwcsで始まる関数。マルチバイトのstrで始まる関数。マルチバイト2バイト文字対応の_mbsで始まる関数。全部で22種類。引数は文字列コピーと同じですが、コピー先の変数が既に文字列が格納されてる\0で終わる文字列になっていて、処理をした結果、第一引数の文字列先頭アドレスの中身が文字列連結した結果になるという点が異なると覚えれば、連結とコピーは似ていると覚えればよいかと思います。先の文字列コピーのプログラムのコピーcpy系関数の後ろに連結cat系関数を追加して、文字列を2回繰り返されるようにしたサンプルです。所謂(いわゆる)、手抜きです。ただし、連結関数では連結後の文字列の配列大きさを要求されるため、あらかじめ動的に確保する領域が2倍になっていることに注意して下さい。<br /> <syntaxhighlight lang="cpp" line start="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]) * 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; } </syntaxhighlight> 出力結果<br /> <syntaxhighlight lang="cpp"> _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>, </syntaxhighlight> =='''文字列比較'''== 数値の比較は配列でないかぎりは単純な1変数同志の比較ですが、文字列は配列全体が一致しているかを確認することになります。かといって、自分で配列全体を相互に比較するようなプログラムを組むという必要はなく、標準で準備されている関数を使う事で比較できます。VB、PHP、Perlのような関数では関数を使わずに、数値と数値を1変数で比較するかのように比較ができる仕組みになっていますが、C言語では関数を使って比較する必要があります。面倒だ(´Д`;)。<br /> この厳密さも、C言語を難しく感じる一つの要素だと思います。配列をひとつづつ確認する作業なんだという意識づけを忘れないという意味では大事なようにも感じます。だから、ちょっとした変更の伴う比較でも、どうすればよいかを考えることができるのだと思います。VBやPHP、Perlのような便利な比較ばかりをやっているとふと、大文字小文字区別なし変換をしたいとか、半角全角区別なし変換をしたいときに、思考が止まってしまう。 そういうときにはPHPやPerl、VBでも一生懸命調べて、C言語の考え方にたどり着いて、比較することになるんでしょうけど…さいしょから、文字列比較とは、こういうものだと知っておけば、それでいいのですから、ネガティブに考えず、これを覚えれば、潰しが効くとポジティブにとらえてやっていきましょう。比較は英語でcompairと表現するため、関数名にはcmpが使われます。半導体製造工程のcmpとは違います。Chemical Micro Polisherだっけ?違った。Chemical Mechanical Polishingだった。<br /> 例によって比較の関数もマルチバイト文字、ワイド文字、マルチバイト2バイト文字対応といろいろな関数があります。<br /> strncmp、wcsncmp、_mbsncmp、_mbsncmp_l<br /> http://msdn.microsoft.com/ja-jp/library/vstudio/eywx8zcx.aspx<br /> _strnicmp、_wcsnicmp、_mbsnicmp、_strnicmp_l、_wcsnicmp_l、_mbsnicmp_l<br /> http://msdn.microsoft.com/ja-jp/library/vstudio/chd90w8e.aspx<br /> _stricmp、_wcsicmp、_mbsicmp、_stricmp_l、_wcsicmp_l、_mbsicmp_l<br /> http://msdn.microsoft.com/ja-jp/library/vstudio/k59z8dwe.aspx<br /> strcmp、wcscmp、_mbscmp<br /> http://msdn.microsoft.com/ja-jp/library/vstudio/e0z9k731.aspx<br /> _mbsnbicmp、_mbsnbicmp_l <br /> http://msdn.microsoft.com/ja-jp/library/vstudio/dyhkb6c5.aspx<br /> _mbsnbcmp、_mbsnbcmp_l <br /> http://msdn.microsoft.com/ja-jp/library/vstudio/hce588f2.aspx<br /> 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<br /> http://msdn.microsoft.com/ja-jp/library/vstudio/a7cwbx4t.aspx<br /> http://msdn.microsoft.com/ja-jp/library/vstudio/2w46a1da.aspx<br /> http://msdn.microsoft.com/ja-jp/library/vstudio/72c43s4t.aspx<br /> http://msdn.microsoft.com/ja-jp/library/vstudio/a697c234.aspx<br /> <br /> 多い!更に多い。<br /> けどicmpってついてるやつは、大文字小文字を区別しないと考えればいいし、cmpで終わらない、collはコードページという特殊な考え方が使われること、ncpmは比較する文字数指定あり、_lはロケール個別指定、と考えればかなりすっきり。collによる比較はあとで考えましょう。<br /> <syntaxhighlight lang="cpp" line start="1"> #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; } </syntaxhighlight> 出力結果 <syntaxhighlight lang="cpp"> _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, </syntaxhighlight> と、こんな感じです。結果を見るとわかるのですが、1,-1,0で結果が表現され、先頭から比較して、一番最初に異なる文字同志を比較したときの結果、文字コードの値が大きかったか小さかったかで、1と-1とに結果が分かれます。これを使って昇順に並べたりすることも出来ます。もちろん0が返ってきたときは、完全一致です。等しかったということになります。<br /> 少し変わっているのは、関数によっては、文字コードの差分値を返すものもあるという点です。_mbs系では文字コードの差分を返すことは無いようです。cmpの前にiが付く関数は差分を返すようです。ただし_mbsnicmpや_mbsicmpのような関数は差分は返しません。wcs系の関数は差分を返します。このことで差分が数値コードによる部分である場合は、その差は数字の差でもあり、一文字(一桁)の数学的な差を計算するのと同様の処理になったり、アルファベットによる連番と考えた場合でも、その差を計算していることにもなります。2バイト文字では差が大きな数値になりますが、符号付きint型で十分に収まる値です。今回は実験的にlongを使いましたが、無駄足でした。仕様のとおりに動作しますね。<br /> 但し、差分の件については、仕様に明記されていませんので、実際に利用する関数での動作を確かめてから使う方が良いと思います。仕様に明記されているのは0より大きい値を返すか小さい値を返すかのどちらかとなっています。正確なVisual C++に限らない言語仕様を検索してみたのですが、わかりませんでした。coll系の比較関数は現在のコードページに従いますので、locale情報を指定しない状態では、各PCのコードページに従います。コードページというのはcp932のような具体的な文字コードのことです。cpの後に続く数字で文字コードセットは分類されています。特段の理由がなければ、使うことは無いでしょう。selocale関数でも引数を指定しなければ各PCのコードページに従いますから、setlocaleで指定できない理由があるという特殊なケースになりそうです。それか、cmpという関数が嫌いでcollが良いと思うかとかですね。ひょっとしたら差分戻り値の件で動作が異なるかもしれません。あとで確認をしておきたいと思います。とてつもなく長いですが、その確認をしてみた結果が以下のものでして、<br /> <br /> [[文字列操作 文字列比較 実行結果1]]<br /> <br /> 528行目から534行目のように同じ'Y'0x79大文字と'y'0x89小文字の比較でありながらも、strcollでは1とstrcmpでは-1とでは結果が異なります。asciiコードでは小文字の方が大きな数字の文字コードが割り振られているため、strcmpのように Y と y の差は0x89 - 0x79で負の数値となり -1 となることを期待しますが、collは現在のコードページかつ辞書式順序を使うために小文字よりも大文字が後ということで1になります。辞書式順序って何だ?と思いつつあるのが131202時点の状況でして、さらに調査をすすめたものが<br /> <br /> [[文字列操作 文字列比較 実行結果2]]<br /> <br /> で、 *大小のみの判定<br /> :wcscmp<br /> :<br /> :wcsicoll/wcscoll/wcsnicoll/_wcsncoll<br /> :<br /> :strcmp/strncmp<br /> :<br /> :strcoll/stricoll/<br /> :<br /> :_mbscmp/_mbsicmp/_mbsncmp/_mbsnicmp/_mbscoll/_mbsicoll/_mbsncoll/_mbsnicoll<br /> <br /> :但し、半角スペースと-全角の比較が発生すると符号付き4byteの最大値を返す。2147483647=21億4748万4647<br /> :_strnicoll/_strncoll<br /> <br /> *差分を返す<br /> :wcsicmp/wcsnicmp/wcsncmp/<br /> <br /> :stricmp/_strnicmp<br /> <br /> という具合の動作であります。collの特徴的なのは辞書順と呼んでいる比較の概念だと思います。ASCIIコードでは大文字と小文字とでは、小文字の方が大きい文字コード番号が付与されていますが、wcs**coll系の比較をする関数では、小文字の方が値が小さいものとして判定してくれます。漢字の範囲になるとロケールで指定したcp932の文字コード順で比較してくれます。阿という文字と哀という文字はUnicode(UTF16)では阿-0x963F 哀-0x54C0 表-0x8868であり、Shift_JISのcp932では阿-0x88A2 哀-0x88A3 表-0x955Cと定義されていますから、阿<nowiki><</nowiki>哀<nowiki><</nowiki>表 のように比較をしてくれます。coll系の関数を使わない場合は 哀<nowiki><</nowiki>表<nowiki><</nowiki>阿のように比較されます。この阿や哀や表という名前のファイル名のテキストをWindowsのエクスプローラで昇順表示すると、この順番になることも確認できます。Shift_JISコード順でソートされてるんだなぁと確認が出来ると思います。Cp932の半角記号あたりの辞書順ってのは、どうなってるんでしょうね。これもまた今度しらべてみたいと思います。ファイル名に使えない文字あたりはどんな順番なんだろうか? '''★豆知識''' '''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の符号化方式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文字のコード表には表現できないですね。 =='''文字列区切り文字分割'''== 文字列の中に区切りがある場合ってのは、例えば、<br /> 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/後藤まりこ,海と花束/きのこ帝国<br /> のようなヒットチャートベスト50があったとしたら , カンマでチャートが区切られていて、 / スラッシュで曲名とアーチスト名が区切られていることになります。<br /> こういった区切り文字毎に見ると意味が発生する最小の単位をトークンと言います。区切る文字によっては更に小さな意味を持つこともありますが、区切り文字毎に文字を抜き出そうとする文字分割処理そのものをトークンと呼んでいます。従って、関数名にはtokenのtokをとったような関数名が使われます。これまでの関数もそうでしたが、やっぱりいろんな種類のstrtok関数が準備されていまして…<br /> strtok、_strtok_l、wcstok、_wcstok_l、_mbstok、_mbstok_l ,<br /> http://msdn.microsoft.com/ja-jp/library/vstudio/2c8d19sb.aspx<br /> strtok_s、_strtok_s_l、wcstok_s、_wcstok_s_l、_mbstok_s、_mbstok_s_l<br /> http://msdn.microsoft.com/ja-jp/library/vstudio/ftsafwz3.aspx<br /> 比較関数よりは少ないけど、やっぱりマルチバイト版、マルチバイト2バイト文字対応版、ワイド文字版、それに_sをつけた文字列長を指定するセキュア版と個別ロケール指定をする_lのロケール版があります。_l版についてはココまで使い方を触れてこなかったのですが、 <syntaxhighlight lang="cpp"> _locale_t locale; locale = _tsetlocale(LC_ALL, _T("Japanese")); </syntaxhighlight> として、localeという変数に_locale_t型の値を_tsetlocaleの返り値として格納しておいたものを関数の最後の引数にします。<br /> コマンドプロンプトで動作確認してるだけの段階でlocale変数を利用しても、文字化けしか起こせないと思うので、テキスト出力とかを覚えてから試してみるといいかもしれません。<br /> strtok系の関数では主要な引数が2つで、第1引数には区切り文字を含んだ大元の文字列変数の先頭アドレスの値。第2引数には区切り文字の先頭アドレスの値。","のように文字列リテラルを直接指定することもできますし、複数の区切り文字を含めて",/"とすることもできます。このような文字列が格納されてる変数の先頭アドレスを含んだポインタ変数あるいは、アドレスを返す配列名を指定すること(結局ポインタ変数みたいなものですが…)ができます。第1引数の文字列は書き換え可能な変数でないと駄目です。const char[]="…"のように定義している書き換えできない変数を引数にするとエラーになります。なぜならば、この関数は第2引数で指定している区切り文字を発見したら、その文字が格納されているアドレス部分、配列要素部分の中身を文字列終端を意味する\0に書き換えるからです。つまり、第1引数は、元の状態ではなくなるので、あらかじめバックアップが必要ならコピーをして、保管しておかなければなりません。そして、区切り文字が見つかると先頭アドレスから\0に置き換えた部分までの文字列を返り値として処理を終了します。<br /> え?1つ目以降の区切り文字の結果は?<br /> そうですよね…。2回目の区切り文字探索以降について、この関数の使い方は特殊でして、最初の1回目の区切り文字探索時は上記に記述したような引数で良いのですが、2回目以降の区切り文字探索では第1引数にNULLを設定し、最初に探索した際に\0置き換えた区切り文字の次のアドレスから、第2引数で指定した区切り文字を探索します。それで見つかった区切り文字をまた\0に置き換えて、探索開始アドレスから、\0までの文字列を返り値として処理が終わります。<br /> え?それ以降は?<br /> そうですよね…。探索の結果、区切り文字がみつからなくなるまで、繰り返すようなプログラムにして、ひとつづつ掘り起こすという地味な処理になります。 結果を文字列配列に格納したい場合は、自分で文字列配列を準備して、順番に格納しなければならないのです。区切り文字が何回現れて、いくつの文字が区切られた文字列が返ってきたのかも知りたい場合は、自分で回数を記録する処理が必要になります。<br /> Perlなら見つかった分だけ勝手に配列を作ってくれるような関数だったぞ!って思っている人もおられるかもしれませんが、C++ではそこまで便利な標準関数はないです。ご自分でどうぞお好きなように使って下さい。みたいな感じ?そういうことです。2回目以降で元の文字列を指定せず、NULLを指定することからも、察知できることですが、二つ以上の区切り文字を含めた文字列を交互に、作業することもできませんのであしからず。一つ目が終わってから次の文字列って感じです。不思議な仕組みですよね。想像するに、文字列の後ろからなんらかの文字が現れて次に現れる\0がトークン検索開始位置の文字列になってるのかもしれません。このあたりは実験してみることによってあきらかになるかと思います。<br /> 区切り文字として複数の文字を指定できますが、それぞれ1文字が独立した区切り文字ですので、// のような2文字以上で構成される区切り文字はこの関数だけでは対応できません。新しい手法が必要になります。 <syntaxhighlight lang="cpp" line start="1"> #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,閃光 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/後藤まりこ,海と花束/きのこ帝国"); 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]; TCHAR **ppcStrChartTmp = new TCHAR*[iChartCommaCnt]; for(int i = 0; i <= iChartCommaCnt;i++){ *(ppcStrChart + i) = new TCHAR*[1];//曲名とアーチスト名の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)){ 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 ;i++){ delete[] *(ppcStrChartTmp + i); } for(int i = 0;i <= iChartCommaCnt ;i++){ delete[] *(*(ppcStrChart + i) + 0); delete[] *(*(ppcStrChart + i) + 1); } delete[] pcStr0; delete[] pcStr1; delete[] pcStr2; return 0; } </syntaxhighlight> という具合にtcstok_s関数を使えば、並列してトークン処理が進められるようです。第3引数の参照というC++独自の型を使って、47行目 81行目での最初の呼び出し(エスタブリッシュとも表現します。エスタブリッシュショットというと撮影なんかでワンシーンの手前に一枚絵の風景絵を置くことで、時間帯を表現したりする手法を指します。夜景がシーンの前に入れば室内に映像が切り替わっても夜の出来事であるように示唆するものです。)をして、114、117、134行目のように連続して現れるであろう区切り文字を検索します。114,117行目では、もう区切り文字が現れることがないことがわかっているのですが、2個目のトークンを取得するために実行したり、ループ処理を終わらせるために再度、実行したりという手法で使っています。アイデア次第でなんでもありです。最初にカンマ区切りでトークンを取得するのですが、その間で、さらにカンマ区切りトークンの中に必ず一度現れる、スラッシュによるトークン処理を入れこんでいます。区切り文字が違うだけで全く同じ処理です。こういうプログラム記法は普通はしません。通常は、関数のようなサブプログラムにして、関数を呼び出すことで同じ処理になるように記述します。C++の場合は関数でなくても、クラスのメンバ関数にしても良い訳です。結局同じことですが…。<br /> このような下手くそなプログラムを動かした結果が以下の通りになります。 {| style="color:black; background-color:#ffffff;" cellpadding="3" cellspacing="0" border="1" |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割くらい会社が持っていくっていうそんな感じ。研究させてもらえる環境を提供してるだけでもありがたいと思いなさいということです。それが嫌なら自分で会社たててやってみ?ろくなことなんないぜ?ってそんな感じ。自分で音楽事務所たててうまくいく人なんてそうそういないんだよ。オレは、なんかもうそういうのどうでもよくなってきて、惰性で人生を回転させてるだけ。うっかり幸せな気分でも向上するような出来事にたどり着けたら、人生っては、それなり頑張れば、それなりになるってことを確信して死ねるんだろうし、そうでないとしたら、まぁやっぱこんなもんだよな人生ってのはって心の隅で感じて、半笑いくらいで死ねるような気がする。この先も、どうでもいい。<br /> 別にプログラムつくりたいわけでもなし、こんな文書かいてんだから、まぁ分かる人には分かるでしょ。それでいい。マルチバイト版とマルチバイト2バイト対応版の動作も確認したいよね。まぁ時間があったらですね。それと表の先頭に - があったら、空白を付け足す処理と 文字列の中に| があったら、&#x7c;に置き換える処理を入れないと、 mediawikiのXHTML表記の表にならないので、もう少し改良が必要です。今回はたまたま、N'夙川BOYS(ん しゅくがわ ボーイズ)の曲名に , が使われていたので、流石にカンマ区切りのトークン処理を不完全にさせておくのはまずいと思い、ここまでで記述してきた文字列操作だけを使って、対応版のサンプルプログラムを作っておきました。N'夙川BOYS恐るべし!Hello,999か…。曲名って何でもありの自由空間だから規制がない。Unicodeで全部表せたらいいほう。なんかよくわからない記号とか使うこともあるし。なんらかの規制がないとデータベース化する人は大変だろうなぁ。 ※文字列配列を2次元配列に、つまり3次元配列にしたあたりから、動的なメモリの解放処理ができなくなった(;´・д・)ナンデ。スキル不足でした。役に立ちそうもないし、挫折かな。変に動的なメモリ確保に拘ったのはコレが初めてだから、ショウガナイか。できるとおもってたけど出来ない。そういうことなのかもしれない。一番したの層の文字列事態の解放はできるんですけど、アドレスが格納された部分の解放を記述するとコンパイルエラーにはならないけど、実行時にプログラムが止まる。 ほんとは delete[] *(ppcStrChart + i); ってやって50個の配列に要素[0]と[1]っていうのがぶら下がっているのを解放して、最後に delete[] ppcStrChart ってな具合にしなきゃいけいないはずなんですけど…strtokを使うとゴミが残りやすいのかな?解放できなくなる。生成した直後なら消せるから、やっぱなんかあるなコレ。リファレンス書くつもりがどんどん疑問が増えて課題が増えてるという…無力だな。また時間があるときにでも、原因を調べてみよう。恐るべしstrtok…。しばらくは、OS側にあとかたづけは任せてみる。ごめんなさい。ときどき再起動しとけばいいのかな…(´Д`;) おそらくは、一度生成したものを再作成するかもしれないという作り方に問題があるのかもしれない。文字列の長さが確定してから容量を確保するようなプログラムにするべきなんだろうね。次から気を付ける。って今回のサンプルはなおさないらしい。コイツ。 =='''文字列の型変換'''== =='''文字列文字コード変換'''== =='''文字列大文字小文字変換'''== =='''文字列半角文字全角文字変換'''== =='''文字列ファイルパス操作'''== =='''文字列ファイル名操作'''== =='''文字列の検索と置換'''== =='''文字列ファイルへの入出力'''== *比較 *一致 *区切り文字分割 *探索 *正規表現 regex_match *文字列の型変換 *文字コード変換 *大文字小文字変換 *半角文字全角文字変換 *ファイルパス操作 *ファイル名操作 *拡張子取得 *文字列の検索と置換 *ファイルへの入出力 <!-- {|class="wikitable" !上位ビッツ\下位ビッツ!!0!!1!!2!!3!!4!!5!!6!!7!!8!!9!!A!!B!!C!!D!!E!!F |- |0||NUL||SOH||STX||ETX||EOT||ENQ||ACK||BEL|||BS||HT||LF||VT||FF||CR||SO||SI |- |1||DLE||DC1||DC2||DC3||DC4||NAK||SYN||ETB||CAN||EM||SUB||ESC||IS4||IS3||IS2||IS1 |- |2||SP||!||"||#||$||%||&||'||(||)||*||+||,||-||.||/ |- |3||0||1||2||3||4||5||6||7||8||9||:||;||<||=||>||? |- |4||@||A||B||C||D||E||F||G||H||I||J||K||L||M||N||O |- |5||P||Q||R||S||T||U||V||W||X||Y||Z||[||\||]||^||_ |- |6||`||a||b||c||d||e||f||g||h||i||j||k||l||m||n||o |- |7||p||q||r||s||t||u||v||w||x||y||z||{|| | ||} ||~||DEL |- |} {|class="wikitable" !上位ビッツ\下位ビッツ!!0!!1!!2!!3!!4!!5!!6!!7!!8!!9!!A!!B!!C!!D!!E!!F |- |8||||||BPH||NBH||||NEL||SSA||ESA||HTS||HTJ||VTS||PLD||PLU||RI||SS2||SS3 |- |9||DCS||PU1||PU2||STS||CCH||MW||SPA||EPA||SOS||||SCI||CSI||ST||OSC||PM||APC |- |A||||。||「||」||、||・||ヲ||ァ||ィ||ゥ||ェ||ォ||ャ||ュ||ョ||ッ |- |B||ー||ア||イ||ウ||エ||オ||カ||キ||ク||ケ||コ||サ||シ||ス||セ||ソ |- |C||タ||チ||ツ||テ||ト||ナ||ニ||ヌ||ネ||ノ||ハ||ヒ||フ||ヘ||ホ||マ |- |D||ミ||ム||メ||モ||ヤ||ユ||ヨ||ラ||リ||ル||レ||ロ||ワ||ン||゙||゚ |- |E|||||||||||||||||||||||||||||||| |- |F|||||||||||||||||||||||||||||||| |- -->
C 文字列操作
に戻る。
個人用ツール
ログイン
名前空間
ページ
議論
変種
表示
閲覧
ソースを表示
履歴表示
操作
検索
案内
メインページ
コミュニティ・ポータル
最近の出来事
最近の更新
おまかせ表示
ヘルプ
ツールボックス
リンク元
関連ページの更新状況
特別ページ