(このコンテンツはメールマガジンの STL & iostream 入門に手を加えたものです。「 STL と iostream が使えるかのチェック」等はメールマガジンの方のページをご覧ください)
|
ポインタの移動 ( #05 ) |
std::strstream を使ってデータを書き込む場合、「どんどん後ろへと追加していく」ことになります。たとえば、「一度書き込みを中断して再び書き込み」をしても、一番最後にそのまま追加します。
//////////////////////////////////////////////////////////////// // 書いて追加。 #include <stdio.h> #include <iostream> #include <strstream> void WriteAndAppend() { char ch[130]; std::strstream cStrStrm( ch, 128, std::ios::out ); cStrStrm << "ABCD"; // 一休み。 cStrStrm << "EFG" // 再び書き込み。 << std::ends; // 終端文字も。 cStrStrm << "HIJ" // さらに書き込み。 << std::ends; printf( "%s\n", ch ); } // 結果 ABCDEFG ///////////////////////////// まず "ABCD" と書き込みます。ここで std::ends は渡していないので、「終端文字( '\0' )」が書き込まれていないことに注意してください。この時点ではまだ文字列は閉じていません。 次に、再び "EFG" と書き込みます。結果のように、この文字列はそのまま "ABCD" の後ろに継なげられています。この最後には「終端文字」を書き込んでおきます。 最後に "HIJ" と書き込みます。が、これは出力結果に現れていません。これは直前の「終端文字」の後ろへと文字を追加しているからです。実際に文字は追加されているのですが、表示するときには「終端文字」までなので、その後ろの "HIJ" が表示されないことになります。 以上の結果から「 std::strstream の文字追加は、終端文字を目安にしているわけではない」ということが分かると思います。 実は std::strstream は、内部に「どこに書き込むか」を示すポインタを持っています。文字列を書き込むとそのポインタを進めて、次の書き込みを待ちます。再び書き込みが行われれば、そのポインタの位置から書き込みを始めます。 このポインタの位置は、 std::strstream の std::strstream::seekp() というメンバ関数を使うことで移動することができます。 //////////////////////////////////////////////////////////////// // ポインタを移動します。 #include <stdio.h> #include <iostream> #include <strstream> void MovePointer() { char ch[130]; std::strstream cStrStrm( ch, 128, std::ios::out ); cStrStrm << "ABCDEFG" << std::ends; printf( "%s\n", ch ); cStrStrm.seekp( 2 ); // 先頭から3文字目へ移動。 cStrStrm << "345"; // std::ends は付けません。 printf( "%s", ch ); } // 結果 ABCDEFG AB345FG ///////////////////////////// まず "ABCDEFG" と文字列を書き込みます。終端文字も追加します。 そのあと std::strstream::seekp() というメンバ関数を呼び出します。引数には「先頭から何文字目か」を示す整数値を渡します(先頭は「 0 」です)。上の例では「 2 」なので、先頭から3文字目、「 C 」の文字の位置に移動したことになります。 ふたたび文字列をコピーします。こんどコピーした "345" は、3文字目から上書きされます。その結果、 "CDE" の文字が "345" に変わっています。また、終端文字は加えていないので、その後ろの "FG" はそのまま残っていて、ちゃんと表示されます。 「 std::strstream 型変数( ここでは cStrStrm )」の中には「どこから書き込むか」ということを示すポインタが入っていて、std::strstream::seekp() を使うことでそのポインタを移動できるというわけです。 |
ポインタの位置 ( #06 ) |
前回使用した std::strstream::seekp() は「相対位置」で移動することもできます。「一番最初から」「一番最後から」「今いる位置から」移動することができます。
//////////////////////////////////////////////////////////////// // ポインタを相対位置で移動します。 #include <stdio.h> #include <iostream> #include <strstream> void MovePointerRelativity() { char ch[130]; std::strstream cStrStrm( ch, 128, std::ios::out ); cStrStrm << "ABCDEFG" << std::endl << std::ends; printf( "%s", ch ); cStrStrm.seekp( 1, std::ios::beg ); // 先頭から2文字先へ移動。 cStrStrm << '2'; cStrStrm.seekp( -( 128 - 7 + 2 ), std::ios::end ); // 後ろから2。 cStrStrm << '6'; cStrStrm.seekp( -3, std::ios::cur ); // そこから2文字前へ。 cStrStrm << '4'; printf( "%s", ch ); } // 結果 ABCDEFG A2C4E6G ///////////////////////////// 「一番最初から」を示すフラグは std::ios::beg です。「 std::ios:: 」はおなじみですね、前に出てきた std::ios::right などと同じです。このフラグを第2引数に渡すと、第1引数の整数が「先頭からの位置」という意味になります。これは「第2引数を省略した std::strstream::seekp() 」と同じです。 「一番最後から」を示すフラグは std::ios::end です。ここでの「一番最後」は「 cStrStrm を作ったときに第2引数に渡した値」だということに注意してください。「現在のポインタ」とか「終端文字のある場所」とかと間違えてしまわないでください。また、このように移動距離はマイナスも使えます。 [注:ライブラリの中には std::ios::end が「終端文字のある場所」から移 動するものもありました。ご注意ください] 「今いる位置から」を示すフラグは std::ios::cur です。このときは"ABCDEFG" の "G" にポインタがあるので、そこからの3つ前に移動しています。現在位置からの相対移動をするときには、このフラグを使います。 |
|
「あれ、なんで "G" にポインタがあるの?」と思うかもしれません。書き込みポインタは常に「次に書き込む位置」を指すようになっています。そのため、 "F" の位置に '6' を書き込んだとき、ポインタがひとつ進んでいるんです。 これを確認するため、 std::strstream::tellp() というメンバ関数を使ってみましょう。これを使うと「今どこにポインタがあるか」が分かります。 //////////////////////////////////////////////////////////////// // ポインタの位置を取得して、移動します。 #include <stdio.h> #include <iostream> #include <strstream> void TellPointer() { char ch[130]; std::strstream cStrStrm( ch, 128, std::ios::out ); std::streampos lPos = cStrStrm.tellp(); // 現在位置を取得。 printf( "%d\n", (long)lPos ); cStrStrm << "ABCDEFG"; lPos = cStrStrm.tellp(); // 現在位置を取得。 printf( "%d\n", (long)lPos ); cStrStrm.seekp( 3 ); // 先頭から4文字目に移動。 lPos = cStrStrm.tellp(); // 現在位置を取得。 printf( "%d\n", (long)lPos ); } // 結果 0 7 3 ///////////////////////////// std::strstream::tellp() は現在のポインタの位置を取得するメンバ関数です。この関数を呼び出すと、現在のポインタの位置が返ってきます。返ってきた値は std::streampos というクラスの変数で受け取ります。中には現在のポインタの位置が格納されています。この例で (long) としているのは、そのポインタの位置を出力するためです。 [注:ライブラリによっては std::streampos がクラスでないのものもあります。ご注意ください] この値から、実際のポインタの位置が分かると思います。書き込みをしてないときには、ポインタは先頭に来ています。 "ABCDEFG" を書き込むと、ポインタは先頭から8文字目、つまり "ABCDEFG" の次へと来ていることが分かります。また、 std::strstream::seekp() で移動すればもちろん同じ位置が返ってきます。 |
(C)KAB-studio 2000 ALL RIGHTS RESERVED. |