Version 12.02
数字と文字列の変換
「今回は、数値と文字列の変換について説明します」
『それって結構重要?』
「重要。たとえば 01.txt から 10.txt までのファイルを読み込む、ってい
う時にべたに書くよりは」
『 for 使って数字に変換した方が楽ってことね』
「そういうこと。そういう場合のためにってことで。まず、基本的な考え方
から。たとえば」
int i = 100;
「ってあった場合、 int の中には 0x00000064 が入っています。これを」
char pch[256];
「の中に文字列として 100 が入ってる場合」
pch[0] == '1' == 0x31
pch[1] == '0' == 0x30
pch[2] == '0' == 0x30
pch[3] == \0 == 0x00
「という感じになっていればいいわけ」
『それぞれの文字コードが1文字ずつ入ってる……でも int とかなり違う
よね』
「そう。でも、簡単に変換できると思わない?」
『どうやって?』
「まず、 100 を1桁ずつに分けて考えます」
『んー、最初の 1 だとすると、それを pch[0] に入れるわけで、それが 0x
31 になればよくて、…… 0 の時は 0x30 だから……あ、 0x30 足して入れ
ればいいの?』
「そういうこと。1桁ずつ取り出して 0x30 足すとそれが文字コードの 0
や 1 になるから、そうしたら1文字ずつ入れていけばいいわけ」
『なるほど、簡単そう』
「実際にやると大変なんだけど」
『へ?』
「1文字ずつ取り出すの、どうすればいいと思う?」
『えーっと……』
「こうします」
void GetFirst()
{
int i = 105;
// 5 だけ抜き出します。
int i0 = i - ( ( i / 10 ) * 10 );
TRACE( "%d\n", i0 );
// 5
}
『 10 で割って 10 で掛ける? それって一緒になるんじゃ』
「じゃないでしょ、 105 を 10 で割って 10 、 10 を掛けて 100 」
『1の位が 0 に……そか、それで引くと最初の桁だけ残るんだ!』
「そういうこと。で、これは1の位だけだから、実際のプログラムでは全部
の桁でしなきゃいけないし、桁数も取得しなきゃいけない」
『めんどー』
「だから普通はランタイム使います」
『……あるんじゃん』
「もちろん。というか使ったことあるでしょ。 sprintf() とか」
『あ! そういえば』
「 Version 5.07 ( No.072 ) でやったね。それに、 CString なら
CString::Format() ってメンバ関数があるからそっちの方がいいかも」
『どう違うの?』
「ほとんど同じ。
void Use_Format()
{
CString cStr;
cStr.Format( "%d", 100 );
TRACE( "%s\n", cStr );
// 100
}
『 %d とか、同じだね』
「 sprintf() も CString::Format() も、 TRACE() とかと同じ書式だか
ら」
『ひとつ憶えれば大丈夫ってわけね』
「でも、微妙な部分もあるかな」
『微妙?』
「このタイプのものの一番の問題は、型のチェックがされないっていう点。
たとえば」
void Use_Format_Bad()
{
CString cStr;
cStr.Format( "%s", 100 );
// アクセス違反。
}
『あ、 Access Violation だ』
「つまり、 100 って数字を文字列のポインタと見なしちゃうから」
『げ、それってまずいじゃん』
「そう、まずいわけ。これは TRACE() でも起きるよ」
void Use_TRACE_Bad()
{
TRACE( "%s\n", 100 );
// アクセス違反。
}
『ホントだ起きる……』
「これは TRACE() や sprintf() や CString::Format() が【可変長引数】
っていうのを使ってるから」
『かへんちょーひきすう?』
「その可変長引数のおかげで、いくつつでも引数を渡すことができるんで
す」
『あ……つまりそれって、オーバーロードとか使ってないってこと?』
「いろんな型の引数をいくつも、なんて考えたらすごい数の組み合わせにな
っちゃうでしょ」
『確かに。だからその可変長引数ってのを使ってるわけね。で、それは型の
チェックができない、と』
「ありとあらゆる型が渡されるんだからしょうがないんだけどね。で、そう
いった場合に代わりになるのが Version 5.17 ( No.082 ) で教えた
std::strstream とか」
『あー、ストリーム系ね』
「こっちはちゃんと型チェックされてるから大丈夫。ただ、使われてる割合
はかなり少ないから」
『少なくても、使っちゃえばいいじゃん』
「前も言ったけど、 C 言語しか使えないときもあるから。これは C++ でし
か使えないからね」
『……他の人と合わせなきゃとか、そーゆーのね』
「でも使えるんなら使った方がいいから。安全性ならこっちの方が上だか
ら」
『はーい』
「さて、ここまでは int を文字列にしてみました」
『……逆?』
「そう、今度は文字列を int にします」
『考え方って同じだよね。それぞれの文字で……今度は 0x30 を引けばいい
のかな』
「そうだね、文字を右から見ていって、 0x30 を引いて、あとは文字の位置
に合わせて 10 の何乗っていうのを掛ければいいかな」
『 10 の何乗?』
「右から2桁目なら 10 、3桁目なら 100 、4桁目なら 1000 を掛けて足
せば」
『そっか、それでできちゃうんだ』
「こっちの変換の方が簡単……と思うかもしれないけど、実はそうでもない
んです」
『ないの?』
「まぁその前に、まずランタイムを使った例から」
void Use_atoi()
{
int i = atoi( "100" );
TRACE( "%d\n", i );
// 100
}
「こんな風に、ランタイムは atoi() を使えば変換できます」
『へー、簡単! でも〈えーとぅーあい〉って変な名前……』
「呼び方は色々あると思うけどね。 ASCII to Int の略だと思うよ」
『 ASCII ?』
「ほら、普通の英文字を ASCII 文字って言うって」
『 Version 5.04 ( No.069 ) でやったね。そか、普通の文字から数字に変
換、って意味ね』
「で、この関数にはひとつ問題があります」
void Use_atoi_Bad()
{
int i = atoi( "a" );
TRACE( "%d\n", i );
// 0
}
『ん? 数字以外が入ってると 0 になるんだ』
「もし "0" を変換したら?」
『 0 ……どっちも 0 ?』
「そう、この関数は、 0 が入ってたのか、それとも変換できなかったのか
がわかんないんです」
『げげげ』
「一応、ランタイムに isdigit() っていうのがあって、これで1文字ずつ
チェックできます」
void Use_isdigit()
{
TRACE( "%d\n", isdigit( '1' ) );
TRACE( "%d\n", isdigit( 'a' ) );
// 4
// 0
}
『 4 ?』
「まぁ、数字だったら 0 以外ってことになってるから、 0 かそうじゃない
かで調べた方がいいかな」
『ん。これ使って……1文字ずつ調べるの?』
「それだと面倒だから、 std::strstream とか使った方がいいかも」
『そっちなら大丈夫? あ、そういえばファイルから取り出した時、 数字
とかじゃなかったら fail() がどうとかあったもんね』
「そういうこと。その辺の仕組みがあるから、ランタイムよりもいいかな。
まぁ関数ひとつで簡単に変換できるのは楽だけど」
『確かに std::strstream とかって使うの結構めんどいよね……』
「その辺も広まってない原因かも。ただ、こういう細かいところではまる事
って多いから、選択は気を付けてね」
/*
Preview Next Story!
*/
『なんか今回って復習って感じ』
「それだけ憶えてきたことも多いってこと」
『そ?』
「でも憶えることはまだまだいっぱいあります」
『げ』
「というわけで次回」
< Version 12.03 パソコンは計算機 >
『につづく!』
「次回からはかなり難しいです!」
『あう〜』