iostream ルーチンを使用するには、ライブラリの使用部分に対応するヘッダーファイルをインクルードしなければなりません。次の表で各ヘッダーファイルについて説明します。
表 3-1 iostream ルーチンのヘッダーファイル
ヘッダーファイル |
内容 |
---|---|
iostream.h |
iostream ライブラリの基本機能の宣言。 |
fstream.h |
ファイルに固有の iostream と streambuf の宣言。この中で iostream.h をインクルードします。 |
strstream.h |
文字型配列に固有の iostream と streambuf の宣言。この中で iostream.h をインクルードします。 |
iomanip.h |
マニピュレータ値の宣言。マニピュレータ値とは iostream に挿入または iostream から抽出する値で、特別の効果を引き起こします。 |
stdiostream.h |
(旧形式) 標準入出力の FILE 使用のための iostream と streambuf の宣言。この中で iostream.h をインクルードします。 |
stream.h |
(旧形式) C++ バージョン 1.2 の旧形式ストリームと互換性を保つための宣言。この中で iostream.h、fstream.h、iomanip.h、stdiostream.h をインクルードします。 |
これらのヘッダーファイルすべてをプログラムにインクルードする必要はありません。自分のプログラムで必要な宣言の入ったものだけをインクルードします。iostream ライブラリは libC の一部で、CC ドライバによって自動的にリンクされます。
iostream を使用した出力は、通常、左シフト演算子 ( << ) を多重定義したもの (iostream の文脈では挿入演算子といいます) を使用します。ある値を標準出力に出力するには、その値を定義済みの出力ストリーム cout に挿入します。たとえば someValue を出力するには、次の文を標準出力に挿入します。
cout << someValue;
挿入演算子は、すべての組み込み型について多重定義されており、someValue の値は適当な出力形式に変換されます。たとえば someValue が float 型の場合、<< 演算子はその値を数字と小数点の組み合わせに変換します。float 型の値を出力ストリームに挿入するときは、<< を float 型挿入子といいます。一般に X 型の値を出力ストリームに挿入するときは、<< を X 型挿入子といいます。出力形式とその制御方法については、ios(3C++) のマニュアルページを参照してください。
iostream ライブラリでは、ユーザー定義型については検知しません。したがってユーザー定義型を出力したい場合は、ユーザーが自分で挿入子を正しく定義する、すなわち << 演算子を多重定義する必要があります。
<< 演算子は反復使用することができます。2 つの値を cout に挿入するには、次の例のような文を使用することができます。
cout << someValue << anotherValue;
上の例では、2 つの値の間に空白が入りません。空白を入れたい場合は、次のようにします。
cout << someValue << " " << anotherValue;
<< 演算子は、組み込みの左シフト演算子と同じ優先順位を持ちます。他の演算子と同様に、括弧を使用して実行順序を指定することができます。実行順序をはっきりさせるためにも、括弧を使用するとよい場合がよくあります。次の 4 つの文のうち、最初の 2 つは同じ結果になりますが、後の 2 つは異なります。
cout << a+b; // + は << より優先順位が高い cout << (a+b); cout << (a&y); // << は & より優先順位が高い cout << a&y; // (cout <<a) & y となっておそらくエラーになる
次のコーディング例では string クラスを定義しています。
#include <stdlib.h> #include <iostream.h> class string { private: char* data; size_t size; public: (この項には直接関係のないさまざまな関数定義) friend ostream& operator<<(ostream&, const string&); friend istream& operator>>(istream&, string&); };
この例では、挿入演算子と抽出演算子をフレンド定義しておく必要があります。string クラスのデータ部が非公開だからです。
ostream& operator<< (ostream& ostr, const string& output) { return ostr << output.data;}
上は、string クラスに対して多重定義された演算子関数 operator<< の定義です。
cout << string1 << string2;
operator<< は、最初の引数として ostream& (ostream への参照) を受け取り、同じ ostream を返します。このため、次のように 1 つの文で挿入演算子を続けて使用することができます。
operator<< を多重定義するときは、iostream ライブラリからエラーが通知されることになるため、特にエラー検査を行う必要はありません。
エラーが起こると、エラーの起こった iostream はエラー状態になります。その iostream の状態の各ビットが、エラーの大きな分類に従ってセットされます。iostream で定義された挿入子がストリームにデータを挿入しようとしても、そのストリームがエラー状態の場合はデータが挿入されず、そのストリームの状態も変わりません。
一般的なエラー処理方法は、メインのどこかで定期的に出力ストリームの状態を検査する方法です。そこで、エラーが起こっていることが分かれば、何らかの処理を行います。この章では、文字列を出力してプログラムを中止させる関数 error をユーザーが定義しているものとして説明します。関数 error はユーザー定義の関数で、定義済みの関数ではありません。関数 error の内容については、 「入力エラーの処理」を参照してください。iostream の状態を調べるには、演算子 ! を使用します。次の例に示すように、iostream がエラー状態の場合はゼロ以外の値を返します。
if (!cout) error( "output error");
エラーを調べるにはもう 1 つの方法があります。 ios クラスでは、operator void *() が定義されており、エラーが起こった場合は NULL ポインタを返します。したがって、次の文でエラーを検査することができます。
if (cout << x) return ; // 正常終了のときのみ返す
また、次のように ios クラスのメンバー関数 good を使用することもできます。
if ( cout.good() ) return ; // 正常終了のときのみ返す
enum io_state { goodbit=0, eofbit=1, failbit=2, badbit=4, hardfail=0x80} ;
エラー関数の詳細については、iostream のマニュアルページを参照してください。
多くの入出力ライブラリと同様、iostream も出力データを蓄積し、より大きなブロックにまとめて効率よく出力します。出力バッファをフラッシュしたければ、次のように特殊な値 flush を挿入するだけで、フラッシュすることができます。
cout << "This needs to get out immediately." << flush ;
flush は、マニピュレータと呼ばれるタイプのオブジェクトの 1 つです。マニピュレータを iostream に挿入すると、その値が出力されるのではなく、何らかの効果が引き起こされます。マニピュレータは実際には関数で、ostream& または istream& を引数として受け取り、そのストリームに対する何らかの動作を実行した後にその引数を返します (「マニピュレータ」を参照してください)。
ある値をバイナリ形式のままで出力するには、次の例のようにメンバー関数 write を使用します。次の例では、x の値がバイナリ形式のまま出力されます。
cout.write((char*)&x, sizeof(x));
この例では、&x を char* に変換しており、型変換の規則に反します。通常このようにしても問題はありませんが、x の型が、ポインタ、仮想メンバー関数、またはコンストラクタの重要な動作を要求するものを持つクラスの場合、上の例で出力した値を正しく読み込むことができません。
iostream を使用した入力は、iostream を使用した出力と同じです。入力には、抽出演算子 >> を使用します。次の例のように、挿入演算子と同様に繰り返し指定することができます。
cin >> a >> b ;
この例では、標準入力から 2 つの値が取り出されます。他の多重定義演算子と同様に、使用される抽出子の機能は a と b の型によって決まります (a と b の型が異なれば、別の抽出子が使用されます)。入力データのフォーマットとその制御方法についての詳細は、ios(3C++) のマニュアルページを参照してください。通常は、先頭の空白文字 (スペース、改行、タブ、フォームフィードなど) は無視されます。
ユーザーが新たに定義した型のデータを入力するには、出力のために挿入演算子を多重定義したのと同様に、その型に対する抽出演算子を多重定義します。
クラス string の抽出演算子は次のコーディング例のように定義します。
istream& operator>> (istream& istr, string& input) { const int maxline = 256; char holder[maxline]; istr.get(holder, maxline, '¥n'); input = holder; return istr; }
get 関数は、入力ストリーム istr から文字列を読み取ります。読み取られた文字列は、maxline-1 バイトの文字が読み込まれる、新しい行に達する、EOF に達する、のうちのいずれかが発生するまで、holder に格納されます。データ holder は NULL で終わります。最後に、holder 内の文字列がターゲットの文字列にコピーされます。
規則に従って、抽出子は第 1 引数 (上の例では istream& istr) から取り出した文字列を変換し、常に参照引数である第 2 引数に格納し、第 1 引数を返します。抽出子とは、入力値を第 2 引数に格納するためのものなので、第 2 引数は必ず参照引数でなければなりません。
この定義済み抽出子は問題が起こる可能性があるため、ここで説明しておきます。この抽出子は次のように使用します。
char x[50]; cin >> x;
上の例で、抽出子は先頭の空白を読み飛ばし、次の空白文字までの文字列を抽出して x にコピーします。次に、文字列の最後を示す NULL 文字 (0) を入れて文字列を完成します。ここで、入力文字列が指定した配列からあふれる可能性があることに注意してください。
さらに、ポインタが、割り当てられた記憶領域を指していることを確認する必要があります。次に示すのは、よく発生するエラーの例です。
char * p; // 初期化されていない cin >> p;
入力データが格納される場所が特定されていません。これによって、プログラムが異常終了することがあります。
char 型の抽出子を使用することに加えて、次に示すいずれかの形式でメンバー関数 get を使用することによって、1 文字を読み取ることができます。
char c; cin.get(c); // 入力に失敗した場合は、c は変更なし int b; b = cin.get(); // 入力に失敗した場合は、b を EOF に設定
他の抽出子とは異なり、char 型の抽出子は行頭の空白を読み飛ばしません。
空白だけを読み飛ばして、タブや改行などその他の文字を取り出すようにするには、次のようにします。
int a; do { a = cin.get(); while( a == ' ' );
メンバー関数 write で出力したようなバイナリの値を読み込むには、メンバー関数 read を使用します。次の例では、メンバー関数 read を使用して x のバイナリ形式の値をそのまま入力します。次の例は、先に示した関数 write を使用した例と反対のことを行います。
cin.read((char*)&x, sizeof(x));
メンバー関数 peek を使用するとストリームから次の文字を抽出することなく、その文字を知ることができます。使用例を次に示します。
if (cin.peek() != c) return 0;
デフォルトでは、iostream の抽出子は先頭の空白を読み飛ばします。skip フラグをオフにすれば、先頭の空白を読み飛ばさないようにすることができます。次の例では、cin の先頭の空白の読み飛ばしをいったんオフにし、後にオンに戻しています。
cin.unsetf(ios::skipws); // 先頭の空白の読み飛ばしをオフに設定 . . . cin.setf(ios::skipws); // 先頭の空白の読み飛ばしをオンに再設定
iostream のマニピュレータ ws を使用すると、空白の読み飛ばしが現在オンかオフかに関係なく、iostream から先頭の空白を取り除くことができます。次の例では、iostream istr から先頭の空白が取り除かれます。
istr >> ws;
通常は、第 1 引数が非ゼロのエラー状態にある場合、抽出子は入力ストリームからのデータの抽出とエラービットのクリアを行わないでください。データの抽出に失敗した場合、抽出子は最低 1 つのエラービットを設定します。
出力エラーの場合と同様、エラー状態を定期的に検査し、非ゼロの状態の場合は処理の中止など何らかの動作を起こす必要があります。演算子 ! は、iostream のエラー状態を検査します。たとえば次のコーディング例では、英字を入力すると入力エラーが発生します。
#include <unistd.h> #include <iostream.h> void error (const char* message) { cout << message << "¥n" ; exit(1); } main() { cout << "Enter some characters: "; int bad; cin >> bad; if (!cin) error("aborted due to input error"); cout << "If you see this, not an error." << "¥n"; return 0; }
クラス ios には、エラー処理に使用できるメンバー関数があります。詳細はマニュアルページを参照してください。
C++ でも stdio を使用することができますが、プログラムで iostream と stdio とを標準ストリームとして併用すると、問題が起こる場合があります。たとえば stdout と cout の両方に書き込んだ場合、個別にバッファリングされるため出力結果が設計したとおりにならないことがあります。stdin と cin の両方から入力した場合、問題はさらに深刻です。個別にバッファリングされるため、入力データが使用できなくなってしまいます。
標準入力、標準出力、標準エラーに関するこのような問題を解決するためには、入出力に先立って次の命令を実行します。次の命令で、すべての定義済み iostream が、それぞれ対応する定義済みの標準入出力の FILE に結合されます。
ios::sync_with_stdio();
このような結合を行うと、定義済みストリームが結合されたものの一部となってバッファリングされなくなってかなり効率が悪くなるため、デフォルトでは結合されていません。同じプログラムでも、stdio と iostream を別のファイルに対して使用することはできます。すなわち、stdio ルーチンを使用して stdout に書き込み、iostream に結合した別のファイルに書き込むことは可能です。また stdio FILE を入力用にオープンしても、stdin から入力しない限りは cin からも読み込むことができます。