プログラム・プロブレム

第六回 ストリーム・ファイル(C++)


I はじめに

涼(以下S):プログラム・プロブレムもいよいよ第六回になりました。
初(以下H):もう自己紹介はいらないだろう。名前が読めないヤツは以前の回を見てくれ。
S:本編の続きに戻るのも久しぶりですね。
H:ああ、第五回の後にティータイムの1~5が続いたからな。
S:いきあたりばったりで書いてるから、そんなことになるんですが。
H:元々が勢いだけで始まったコーナーだからな。仕方無い。
S:さて、今回はストリームとファイルについての解説です。
H:ストリームの方は最初から使ってたんだが、説明せずに誤魔化してたんだよな。
S:今日はそのへんをちゃんと解説する…予定です。
H:作者のヘタレな文章で理解してもらえるといいんだが。
S:賢明な読者さんの理解力に期待しましょう。

II ストリーム

H:ストリームとは「入出力を単純化した筒状のモノ」だと思ってくれ。
S:それを通して色々出し入れできるわけです。
H:ストリームとして操作をまとめておくことで、入出力先が何でも同じ方法でアクセスできる。
S:よく使われる入出力先は、ファイルとかネットワークなんですよね。
   std::cout << "Some text." << std::endl;
H:実は今まで何気無く使ってたコレ「std::cout」がストリームだ。
S:こっちは出力用に使われるので「出力ストリーム」と言います。
H:入力に使った「std::cin」は「入力ストリーム」だ。
S:出力ストリームはデータを直列化します。逆に入力ストリームは直列化されたデータを元に戻します。
H:ぶっちゃけて言えば、データを一直線に並べるのが出力ストリームで、その逆が入力ストリームだ。
S:以前に出たサンプル(第二回)の中に次のようなものがありました。
   std::cout << "Input +1 = " << (i+1) << std::endl;
H:これは std::cout に文字列「Input +1 = 」、数値 i+1マニピュレータ std::endl の順で渡している。
S:つまり順序良く一直線に並べてるわけです。
H:で、結果は文字列にまとめられるぞ。一直線じゃないと文字列に変換できないから並べたわけだな。
S:そしてストリームが結び付けられた先(この場合は標準出力 = 画面)に出力されます。
H:動いてる状態のプログラムからデータを出力するには、こんな手順が必要なわけだ。
S:文字列にしないと人間には分かりませんからねぇ。
H:文字列以外のデータ(バイナリデータ)の場合はまた別だ。そっちは後日だな。
S:種類としては、画像とか映像とか音声とかのデータがありますね。

H:ちなみに、出力値の整形もできる。そのためにはマニピュレータを使うぞ。
S:それではサンプルコードを見てみましょうか。

III ソースコード 1

01:// iostream.cpp
02:#include <iostream>
03:#include <iomanip>
04:int main(int argc,char* argv[])
05:{
06:  using namespace std;
07:  cout << "test" << endl;
08:  cout << setw( 4 ) << setfill('0') << 12 << endl;
09:  cout << setw( 0 ) << hex << showbase << 12 << endl;
10:  cout << dec << setfill(' ') << 12 << endl;
11:
12:  return 0;
13:}
H:出力しかしてないから比較的シンプルだな。
S:ちなみに実行結果は以下のようになります。
01:test
02:0012
03:0xc
04:12

IV 解説 1

H:01から07行目までは分かるな。iomanip のインクルードがあるくらいだ。
08:  cout << setw( 4 ) << setfill('0') << 12 << endl;
S:08行目には「setw」と「setfill」がありますね。これらがマニピュレータです。
H:「setw(4)」で、最小出力幅を 4文字に設定している。
S:次のデータの文字数が 4文字未満だった場合に、文字を追加して 4文字にするわけです。
H:埋めるのに使う文字を設定するのが「setfill('0')」だ。ここでは「0」を使うように指示している。
S:結果として 4文字になった「0012」が出力されています。
09:  cout << setw( 0 ) << hex << showbase << 12 << endl;
H:09行目の「hex」は、出力する数値を「16進数値」にしろという事だ。
S:私達が普通に使ってるのは「10進数値表現」と呼びます。
H:何故かと言うと、10 ごとに位が一つ上がるからだ。0~9 は一桁だろう?
S:同じように一桁で 0~15 を表すのが 16進数値です。数字は 0~9 しか無いので、残りはアルファベットを使います。
H:つまり 0~9 の次は a、以下 b、c … 最後の 15 は f ってわけだ。
S:それで 16 になると桁が上がって「10」になります。10進 の 10 と紛らわしいので、16進の場合は大抵頭に「0x」を付けます。
H:上の例だと「0x10」が 10進での 16 にあたるわけだ。
S:同じように、コンピュータ業界では 8進や 2進もよく使われます。
H:そういう時は 8進は「0」、2進は「0b」を頭に付けて書くのが一般的だな。
S:これらの「頭に付ける文字列」を「プレフィックス」と呼びますね。
H:で、こういったプレフィックスを付けるマニピュレータが「showbase」なんだ。
S:正しくは「show(表示する)」「base(基数)」なんですが、意味としては上の通りです。
H:「基数」ってのは xx進の「xx」の数のことだ。16進なら「基数」は「16」になるな。
S:こんな感じで出力の03行目は「0xc」となってるわけです。12 は 0xc になるのは大丈夫ですね。
H:10 が 0xa、11 が 0xb だからな。まあ、16進も使ってるうちに慣れる。
10:  cout << dec << setfill(' ') << 12 << endl;
S:それで10行目ですが、ここでもマニピュレータが二つあります。
H:「dec」の方が hex の親類で「10進表現にする」だな。
S:はい。他には「oct」で「8進表現にする」も使えますね。
H:次の「setfill(' ')」は、埋めるための文字を「 」(半角スペース)に戻している。
S:そして 12 を出力します。10進に戻したので今度の出力(04行目)は通常通り「12」ですね。

H:他のマニピュレータなど、ストリームについて詳しく知りたい場合は他サイトを検索してみてくれ。
S:英語ですが、リファレンスが C++ Reference にあります。

V ソースコード 2

H:次はファイルについての説明だ。まずはソースコードだが…
01:// file.cpp
02:#include <fstream>
03:int main(int argc,char* argv[])
04:{
05:  std::ofstream fout("file.test.txt");
06:  fout << "file stream test\nline " << 2;
07:  fout.close();
08:
09:  return 0;
10:}
S:とっても短いですね。
H:まあ、分かりやすくていいんじゃないか?

VI 解説 2

S:今回、問題になるのは05~07行目ですね。
H:02行目のインクルードが fstream なのも注意だがな。
05:  std::ofstream fout("file.test.txt");
S:まず05行目です。新しく「std::ofstream」が出てきています。
H:それに、表記の形式が「型 変数名 = 初期値」じゃないな。
S:はい。この形式は「コンストラクタ」と呼ばれます。「型 変数名(パラメータ…)」になりますね。
H:やってることが変数の初期化なのは、int とかの時と同じだ。
S:今回のは「fout」という変数を作って、「"file.test.txt"」というパラメータで初期化しているんですね。
H:std::ofstream の場合は、パラメータにファイル名を渡すことになってる。
S:それで、file.test.txt という名前のファイルが開かれて fout に結び付けられるわけです。
06:  fout << "file stream test\nline " << 2;
H:もう見慣れた行だな。ストリームへの出力だ。
S:準備した fout に文字列と数値を出力していますね。
H:文字列の途中にある「\n」は「改行」の意味の特殊な表現だ。
S:以前は改行は std::endl マニピュレータで行っていましたが、こちらは文字列の中だけで済みますね。
H:ホントはちょっとだけ動作が違うんだが、改行するだけならどちらの方法を使っても問題無いぞ。
S:他にも特殊な表現として「タブ」の「\t」や「"」文字を文字列内に入れる「\"」などもあります。
H:要は特殊な文字を入れる時には、「\」文字と組み合わせた表現を使うわけだ。
S:ちなみに「\」文字自体を入れたい時は「\」と書かなければなりません。注意ですね。
H:こんな特殊表現のことを「エスケープ文字列」と呼んでいるぞ。上の「\」はそのために使う「エスケープ文字」だ。
07:  fout.close();
S:最後の07行目ですが、ここでは利用したファイルを閉じています。
H:もう fout には書かないことを明示的に命令しているわけだな。
S:実際には fout 変数がスコープから出る時に自動的に同じ処理が行われるので、書かなくてもいい行です。
H:準備したものを片付けるという意味では、書くようにした方が分かりやすい。
S:コードを読む他の人にも、これで書き込み処理は終わりだって分かるので親切ですね。

VII 終わりに

H:以上でストリームとファイルの説明は終わりだ。
S:途中に一部別の話(エスケープ文字列)もありましたけどね。
H:説明が足りてないと感じた時は、それがどこなのか作者までメールで教えてもらうと直すかもしれないぞ。
S:あとはリファレンスを参考にしてもらうとかですか。
H:じゃあ、また次回にな。
S:さよならです。

第六回 終了


一覧に戻る