C++ も C と同様に組み込み型の入出力文はありません。その代わりに、入出力機能はライブラリで提供されています。C++ の標準入出力ライブラリは iostream ライブラリです。
この章では、iostream ライブラリの紹介と使用方法を例を示して説明します。 iostream ライブラリすべてを説明しているわけではないので、詳細は iostream ライブラリに関するマニュアルページを参照してください。
-compat=4 では、iostream ライブラリは libC に組み込まれています。しかし、標準モードを使用する場合は、標準 C++ ライブラリの iostream を使用することをお勧めします。-compat=5 には、個別のライブラリ (libiostream) が用意されています。
iostream ライブラリは、既存のソースと -compat=5 との互換性を保つためのものです。ISO 規格に準拠した新しい iostream ライブラリは、C++ 標準ライブラリ (libCstd) に用意されています。古い iostream を使用しているソースがあって、-compat=5 (デフォルト) でソースをコンパイルする場合は、次のどちらかの方法で iostream ライブラリを使用してください。filename にはソースファイル名を入力してください。
% CC filename -library=iostream または % CC -compat=5 filename -library=iostream
ソースファイルには必ず iostream.h をインクルードするようにしてください。
定義済みの iostream には、次のものがあります。
cin、標準入力と結合しています。
cout、標準出力と結合しています。
cerr、標準エラーと結合しています。
clog、標準エラーと結合しています。
定義済み iostream は、cerr を除いて完全にバッファリングされています。「iostream を使用した出力」と 「iostream を使用した入力」を参照してください。
iostream ライブラリを使用すると、プログラムで必要な数の入出力ストリームを使用することができます。それぞれのストリームは、次のどれかを入力先または出力先とします。
標準入力
標準出力
標準エラー
ファイル
文字型配列
ストリームは、入力のみまたは出力のみと制限して使用することも、入出力両方に使用することもできます。iostream ライブラリでは、次の 2 つの処理階層を使用してこのようなストリームを実現しています。
下層では、単なる文字ストリームであるシーケンスを実現します。シーケンスは、streambuf クラスか、その派生クラスで実現されています。
上層では、シーケンスに対してフォーマット操作を行います。フォーマット操作は istream と ostream の 2 つのクラスで実現されます。これらのクラスはメンバーに streambuf クラスから派生したオブジェクトを持っています。この他に、入出力両方が実行されるストリームに対しては iostream クラスがあります。
標準入力、標準出力、標準エラーは、istream または ostream から派生した特殊なクラスオブジェクトで処理されます。
ifstream、ofstream、fstream の 3 つのクラスはそれぞれ istream、ostream、iostream から派生しており、ファイルへの入出力を処理します。
istrstream、ostrstream、strstream の 3 つのクラスはそれぞれ istream、ostream、iostream から派生しており、文字型配列への入出力を処理します。
入力ストリームまたは出力ストリームをオープンする場合は、どれかの型のオブジェクトを生成し、そのストリームのメンバー streambuf をデバイスまたはファイルに関連付けます。通常、関連付けはストリームコンストラクタで行うので、ユーザーが直接 streambuf を操作することはありません。 標準入力、標準出力、エラー出力に対しては、iostream ライブラリであらかじめストリームオブジェクトを定義してあるので、これらのストリームについてはユーザーが独自にオブジェクトを生成する必要はありません。
ストリームへのデータの挿入 (出力)、ストリームからのデータの抽出 (入力)、挿入または抽出したデータのフォーマット制御には、演算子または iostream のメンバー関数を使用します。
新たなデータ型 (ユーザー定義のクラス) を挿入したり抽出したりするときは一般に、挿入演算子と抽出演算子の多重定義をユーザーが行います。
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 からも読み込むことができます。
定義済みの iostream 以外のストリームへの入出力を行いたい場合は、ユーザーが自分で iostream を生成する必要があります。これは一般には、iostream ライブラリで定義されている型のオブジェクトを生成することになります。ここでは、使用できるさまざまな型について説明します。
ファイル操作は標準入出力の操作に似ています。ifstream、ofstream、fstream の 3 つのクラスはそれぞれ、istream、ostream、iostream の各クラスから派生しています。この 3 つのクラスは派生クラスなので、挿入演算と抽出演算、および、その他のメンバー関数を継承しており、ファイル使用のためのメンバーとコンストラクタも持っています。
fstream のいずれかを使用するときは、fstream.h をインクルードしなければなりません。入力だけ行うときは ifstream、出力だけ行うときは ofstream、入出力を行うときは fstream を使用します。コンストラクタへの引数としてはファイル名を渡します。
thisFile というファイルから thatFile というファイルへのファイルコピーを行うときは、次のコーディング例のようになります。
ifstream fromFile("thisFile"); if (!fromFile) error("unable to open 'thisFile' for input"); ofstream toFile ("thatFile"); if ( !toFile ) error("unable to open 'thatFile' for output"); char c ; while (toFile && fromFile.get(c)) toFile.put(c);
このコードでは次のことを実行します。
fromFile という ifstream オブジェクトをデフォルトモード ios::in で生成し、それを thisFile に結合します。thisFile をオープンします。
生成した ifstream オブジェクトのエラー状態を調べ、エラーであれば関数 error を呼び出します。関数 error は、プログラムのどこか別のところで定義されている必要があります。
toFile という ofstream オブジェクトをデフォルトモード ios::out で生成し、それを thatFile に結合します。
fromFile と同様に、toFile のエラー状態を検査します。
データの受け渡しに使用する char 型変数を生成します。
fromFile の内容を一度に 1 文字ずつ toFile にコピーします。
ファイルの内容を一度に 1 文字ずつコピーすることは実際にはあまり行われません。このコードは fstream の使用例として示したにすぎません。実際には、入力ストリームに関係付けられた streambuf を出力ストリームに挿入するのが一般的です。「streambuf」と sbufpub(3C++) のマニュアルページを参照してください。
オープンモードは、列挙型 open_mode の各ビットの OR をとって設定します。open_mode は、ios クラスの公開部で次のように定義されています。
enum open_mode {binary=0, in=1, out=2, ate=4, app=8, trunc=0x10, nocreate=0x20, noreplace=0x40};
UNIX では binary フラグは必要ありませんが、binary フラグを必要とするシステムとの互換性を保つために提供されています。移植可能なコードにするためには、バイナリファイルをオープンするときに binary フラグを使用する必要があります。
入出力両用のファイルをオープンすることができます。たとえば次のコードでは、 someName という入出力ファイルをオープンして、fstream 変数 inoutFile に結合します。
fstream inoutFile("someName", ios::in|ios::out);
ファイルを指定せずに fstream の宣言だけを行い、後にファイルをオープンすることもできます。次の例では出力用の ofstream toFile を作成します。
ofstream toFile; toFile.open(argv[1], ios::out);
fstream をいったんクローズし、また別のファイルでオープンすることができます。たとえば、コマンド行で与えられるファイルリストを処理するには次のようにします。
ifstream infile; for (char** f = &argv[1]; *f; ++f) { infile.open(*f, ios::in); ...; infile.close(); }
標準出力は整数 1 などのようにファイル記述子が分かっている場合は、次のようにファイルをオープンすることができます。
ofstream outfile; outfile.attach(1);
fstream のコンストラクタにファイル名を指定してオープンしたり、open 関数を使用してオープンしたファイルは、fstream が破壊された時点 (delete するか、スコープ外に出る時点) で自動的にクローズされます。attach で fstream に結合したファイルは、自動的にはクローズされません。
ファイル内の読み込み位置と書き込み位置を変更することができます。そのためには次のようなツールがあります。
streampos は、iostream 内の位置を記憶しておくためのデータ型です。
tellg (tellp) は istream (ostream) のメンバー関数で、現在のファイル内の位置を返します。istream と ostream は fstream の親クラスですので、tellg と tellp も fstream クラスのメンバー関数として呼び出すことができます。
seekg (seekp) は istream (ostream) のメンバー関数で、指定したファイル内の位置を探し出します。
fstream aFile の位置再設定の例を次に示します。
streampos original = aFile.tellp(); //現在の位置の保存 aFile.seekp(0, ios::end); //ファイルの最後に位置を再設定 aFile << x; //データをファイルに書き込む aFile.seekp(original); //元の位置に戻る
seekg (seekp) は、1 つまたは 2 つの引数を受け取ります。引数を 2 つ受け取るときは、第 1 引数は、第 2 引数で指定した seek_dir 値が示す位置からの相対位置となります。次に例を示します。この例では、ファイルの最後から 10 バイトの位置に設定されます。
aFile.seekp(-10, ios::end);
aFile.seekp(10, ios::cur);
テキストストリーム上での任意位置へのシーク動作はマシン依存になります。ただし、以前に保存した streampos の値にいつでも戻ることができます。
iostream では、あるストリームを別のストリームに代入することはできません。
ストリームオブジェクトをコピーすると、出力ファイル内の現在の書き込み位置ポインタなどの位置情報が二重に存在するようになり、それを個別に変更できるという状態が起こります。これは、ストリーム操作を混乱させる可能性があります。
フォーマットの制御については、ios(3C++) のマニュアルページで詳しく説明しています。
マニピュレータとは、iostream に挿入したり、iostream から抽出したりする値で特別な効果を持つもののことです。
引数付きマニピュレータとは、 1 つ以上の追加の引数を持つマニピュレータのことです。
マニピュレータは通常の識別子であるため、マニピュレータの定義を多く行うと可能な名前を使いきってしまうので、iostream では考えられるすべての機能に対して定義されているわけではありません。マニピュレータの多くは、この章の別の箇所でメンバー関数とともに説明しています。
定義済みマニピュレータは 13 個あり、それぞれについては表 3-2 で説明します。表 3-2 で使用している文字の意味は次のとおりです。
i は long 型です。
m は int 型です。
c は char 型です。
istr は 入力ストリームです。
ostr は 出力ストリームです。
表 3-2 iostream の定義済みマニピュレータ
|
定義済みマニピュレータ |
内容 |
---|---|---|
1 |
ostr << dec, istr >> dec |
基数が 10 の整数変換を指定します。 |
2 |
ostr << endl |
復帰改行文字 ('¥n') を挿入して、ostream::flush() を呼び出します。 |
3 |
ostr << ends |
NULL ( 0 ) 文字を挿入。 strstream 使用時に利用し ます。 |
4 |
ostr << flush |
ostream::flush() を呼び出します。 |
5 |
ostr << hex, istr >> hex |
基数が 16 の整数変換を指定します。 |
6 |
ostr << oct, istr >> oct |
基数が 8 の整数変換を指定します。 |
7 |
istr >> ws |
最初に空白以外の文字が見つかるまで (この文字以降は istr に残る)、空白を取り除きます (空白を読み飛ば す)。 |
8 |
ostr << setbase(n), istr >> setbase(n) |
基数が n (0, 8, 10, 16 のみ) の整数変換を指定します。 |
9 |
ostr << setw(n), istr >> setw(n) |
ios::width(n) を呼び出します。フィールド幅を n に設定します。 |
10 |
ostr << resetiosflags(i), istr >> resetiosflags(i) |
i のビットセットに従って、フラグのビットベクトルをクリアします。 |
11 |
ostr << setiosflags(i), istr >> setiosflags(i) |
i のビットセットに従って、フラグのビットベクトルを設定します。 |
12 |
ostr << setfill(c), istr >> setfill(c) |
埋め込み文字 (フィールドのパディング用文字) を c とし ます。 |
13 |
ostr << setprecision(n), istr >> setprecision(n) |
浮動小数点型データの精度を n 桁にします。 |
定義済みマニピュレータを使用するには、プログラムにヘッダーファイル iomanip.h をインクルードする必要があります。
ユーザーが独自のマニピュレータを定義することもできます。マニピュレータには次の 2 つの基本タイプがあります。
引数なしのマニピュレータ
istream&、ostream&、ios& のどれかを引数として受け取り、ストリームの操作が終わるとその引数を返します。
引数付きのマニピュレータ
istream&、ostream&、ios& のどれかと、その他もう 1 つの引数 (追加の引数) を受け取り、ストリームの操作が終わるとストリーム引数を返します。
以下に、それぞれのタイプのマニピュレータの例を示します。
引数なしのマニピュレータは、次の 3 つを実行する関数です。
ストリームの参照引数を受け取ります。
そのストリームに何らかの処理を行います。
その引数を返します。
iostream では、このような関数 (へのポインタ) を使用するシフト演算子がすでに定義されていますので、関数を入出力演算子シーケンスの中に入れることができます。シフト演算子は、値の入出力を行う代わりに、その関数を呼び出します。tab を ostream に挿入する tab マニピュレータの例を示します。
ostream& tab(ostream& os) { return os << '¥t' ; } ... cout << x << tab << y ;
次のコードは、上の例と同じ処理をより洗練された方法で行います。
const char tab = '¥t'; ... cout << x << tab << y;
次に示すのは別の例で、定数を使用してこれと同じことを簡単に実行することはできません。入力ストリームに対して、空白の読み飛ばしのオン、オフを設定したいと仮定します。ios::setf と ios::unsetf を別々に呼び出して、skipws フラグをオンまたはオフに設定することもできますが、次の例のように 2 つのマニピュレータを定義して設定することもできます。
#include <iostream.h> #include <iomanip.h> istream& skipon(istream &is) { is.setf(ios::skipws, ios::skipws); return is; } istream& skipoff(istream& is) { is.unsetf(ios::skipws); return is; } ... int main () { int x,y; cin >> skipon >> x >> skipoff >> y; return 1; }
iomanip.h に入っているマニピュレータの 1 つに setfill があります。setfill は、フィールド幅に詰め合わせる文字を設定するマニピュレータで、次の例に示すように定義されています。
//ファイル setfill.cc #include<iostream.h> #include<iomanip.h> //非公開のマニピュレータ static ios& sfill(ios& i, int f) { i.fill(f); return i; } //公開の適用子 smanip_int setfill(int f) { return smanip_int(sfill, f); }
引数付きマニピュレータは、2 つの部分から構成されます。
1 つはマニピュレータ部分で、これは引数を 1 つ追加します。この前の例では、int 型の第 2 引数があります。このような関数に対するシフト演算子は定義されていませんので、このマニピュレータ関数を入出力演算子シーケンスに入れることはできません。そこで、マニピュレータの代わりに補助関数 (適用子) を使用する必要があります。
もう 1 つは適用子で、これはマニピュレータを呼び出します。適用子は大域関数で、そのプロトタイプをヘッダーファイルに入れておきます。マニピュレータは通常、適用子の入っているソースコードファイル内に静的関数として作成します。マニピュレータは適用子からのみ呼び出されるので、静的関数にして、大域アドレス空間にマニピュレータ関数名を入れないようにします。
ヘッダーファイル iomanip.h には、さまざまなクラスが定義されています。各クラスには、マニピュレータ関数のアドレスと 1 つの引数の値が入っています。iomanip クラスについては、manip(3C++) のマニュアルページで説明しています。この前の例では、 smanip_int クラスを使用しており、ios で使用できます。ios で使用できるということは、 istream と ostream でも使用できるということです。この例ではまた、int 型の第 2 引数を使用しています。
適用子は、クラスオブジェクトを作成してそれを返します。この前の例では、smanip_int というクラスオブジェクトが作成され、そこにマニピュレータと、適用子の int 型引数が入っています。ヘッダーファイル iomanip.h では、このクラスに対するシフト演算子が定義されています。入出力演算子シーケンスの中に適用子関数 setfill があると、その適用子関数が呼び出され、クラスが返されます。シフト演算子はそのクラスに対して働き、クラス内に入っている引数値を使用してマニピュレータ関数が呼び出されます。
下記の例では、マニピュレータ print_hex は次のことを行います。
出力ストリームを 16 進モードにします。
long 型の値をストリームに挿入します。
ストリームの変換モードを元に戻します。
この例は出力専用のため、omanip_long クラスが使用されています。また、int 型でなく long 型でデータを操作します。
#include <iostream.h> #include <iomanip.h> static ostream& xfield(ostream& os, long v) { long save = os.setf(ios::hex, ios::basefield); os << v; os.setf(save, ios::basefield); return os; } omanip_long print_hex(long v) { return omanip_long(xfield, v); }
strstream(3C++) のマニュアルページを参照してください。
stdiobuf(3C++) のマニュアルページを参照してください。
入力や出力のシステムは、フォーマットを行う iostream と、フォーマットなしの文字ストリームの入力または出力を行う streambuf からなります。
通常は iostream を通して streambuf を使用するので、streambuf の詳細を知る必要はありません。ただし、効率をよくするため、または iostream に組み込まれているエラー処理やフォーマットのためなどに必要な場合は、直接 streambuf を使用することができます。
streambuf は文字シーケンス (文字ストリーム) と、シーケンス内を指す 1 つまたは 2 つのポインタとで構成されています。各ポインタは文字と文字の間を指しています。実際には文字と文字の間を指しているわけではありませんが、このように考えておくと理解しやすくなります。streambuf ポインタには次の種類があります。
put ポインタ
次に streambuf から渡す文字の直前を指します。
get ポインタ
次に streambuf から取り出す文字の直前を指します。
streambuf は、このどちらかのポインタ、または両方のポインタを持ちます。
ポインタ位置の操作とシーケンスの内容の操作にはさまざまな方法があります。文字列の操作時に両方のポインタが移動するかどうかは、使用される streambuf の種類によって違います。一般に、キュー形式の streambuf の場合は、get ポインタと put ポインタは別々に移動し、ファイル形式の streambuf の場合は、get ポインタと put ポインタは同時に移動します。キュー形式ストリームの例としては strstream があり、ファイル形式ストリームの例としては fstream があります。
ユーザーは streambuf オブジェクト自体を作成することはなく、streambuf クラスから派生したクラスのオブジェクトを作成します。その例として、filebuf とstrstreambuf とがあります。この 2 つについてはそれぞれ filebuf(3C++) および ssbuf(3C++) のマニュアルページを参照してください。より高度な使い方として、独自のクラスを streambuf から派生させて特殊デバイスのインタフェースを提供したり、基本的なバッファリング以外のバッファリングを行なったりすることができます。sbufpub(3C++) と sbufprot(3C++) のマニュアルページでは、それらの方法について説明しています。
ユーザー用の特殊な streambuf を作成するとき以外にも、上に示したマニュアルページで説明しているように、iostream と結合した streambuf にアクセスして公開メンバー関数を使用したい場合があります。また、各 iostream には、streambuf へのポインタを引数とする定義済みの挿入子と抽出子があります。streambuf を挿入したり抽出したりすると、ストリーム全体がコピーされます。
次の例では、先に説明したファイルコピーとは違う方法でファイルをコピーしています。簡単にするため、エラー検査は省略しています。
ifstream fromFile("thisFile"); ofstream toFile ("thatFile"); toFile << fromFile.rdbuf();
入力ファイルと出力ファイルは、以前の例と同じ方法でオープンします。各 iostream クラスにはメンバー関数 rdbuf があり、それに結合した streambuf オブジェクトへのポインタを返します。fstream の場合、streambuf オブジェクトは filebuf 型です。fromFile に結合したファイル全体が toFile に結合したファイルにコピー (挿入) されます。最後の行は次のように書くこともできます。
fromFile >> (streambuf*)toFile.rdbuf();
上の書き方では、ソースファイルが抽出されて目的のところに入ります。どちらの書き方をしても、結果はまったく同じになります。
C++ では、iostream ライブラリの詳細を説明する多くのマニュアルページがあります。 次に、各マニュアルページの概要を示します。
表 3-3 iostream に関するマニュアルページの概要
マニュアルページ |
概要 |
---|---|
filebuf |
streambuf から派生し、ファイル処理のために特殊化された filebuf クラスの公開インタフェースを詳細に説明します。streambuf クラスから継承した機能の詳細については、sbufpub(3C++) と sbufprot(3C++) のマニュアルページを参照してください。filebuf クラスは、fstream クラスを通して使用します。 |
fstream |
istream、ostream、iostream をファイル処理用に特殊化した ifstream、ofstream、fstream の各クラスの特殊化されたメンバー関数を詳細に説明します。 |
ios |
iostream の基底クラスである ios クラスの各部を詳細に説明します。すべてのストリームに共通の状態データについても説明します。 |
ios.intro |
iostream を紹介し、概要を説明します。 |
istream |
次の各項目を詳細に説明します。 istream クラスに対するメンバー関数で、streambuf から取り出した文字の解釈をサポートする 入力のフォーマット ostream クラスで記述されている位置決め関数 その他の istream に関連した関数 istream に関連したマニピュレータ |
manip |
iostream ライブラリで定義されている入出力マニピュレータを説明します。 |
ostream |
次の各項目を詳細に説明します。 ostream クラスに対するメンバー関数で、streambuf に書き込まれた文字の解釈をサポートする 出力のフォーマット ostream クラスで記述されている位置決め関数 その他の ostream に関連した関数 ostream に関連したマニピュレータ |
sbufprot |
streambuf クラスから派生したクラスをコーディングするプログラマに必要なインタフェースを説明します。公開関数のいくつかは、このマニュアルページでは説明しないため、sbufpub(3C++) のマニュアルページも参照してください。 |
sbufpub |
streambuf クラスの公開インタフェース、特に streambuf の公開メンバー関数について詳細に説明します。このマニュアルページには、streambuf 型のオブジェクトを直接操作したり、 streambuf から派生したクラスが継承している関数を探し出したりするのに必要な情報が含まれています。streambuf からクラスを派生する場合は、sbufprot(3C++) のマニュアルページも参照してください。 |
ssbuf |
streambuf から派生し、文字型配列処理用に特殊化された strstreambuf クラスの公開インタフェースを詳細に説明します。streambuf クラスから継承する機能の詳細については、sbufpub(3C++)のマニュアルページを参照してください。 |
stdiobuf |
streambuf から派生し、標準入出力の FILE 処理のために特殊化された stdiobuf クラスについて最小限の説明をします。streambuf クラスから継承する機能の詳細については、sbufpub(3C++) のマニュアルページを参照してください。 |
strstream |
strstream の特殊化されたメンバー関数を詳細に説明します。これらの関数は、iostream クラスから派生した一連のクラスで実装され、文字型配列処理用に特殊化されています。 |
iostream ライブラリの説明では、一般のプログラミングに関する用語と同じでも意味が異なる語を多く使用します。次の表では、それらの用語が iostream ライブラリの説明で使用される場合の意味を定義します。
表 3-4 iostream の用語
用語 |
意味 |
---|---|
バッファ |
バッファには、2 つの意味があります。1 つは iostream パッケージに固有のバッファで、もう 1 つは入出力一般に適用されるバッファです。 iostream ライブラリに固有のバッファは、streambuf クラスで定義されたオブジェクトです。 一般にいうバッファは、入出力データを効率よく転送するために使用するメモリーブロックを指します。バッファリングされた入出力の場合は、バッファがいっぱいになるか、バッファが強制的にフラッシュされるときまで、データの転送は行われません。 「バッファリングなしのバッファ」とは、上で定義した一般にいうバッファがない streambuf を指します。この章では streambuf を指すバッファという語を使用しないようにしていますが、マニュアルページや他の C++ のマニュアルでは、streambuf の意味でバッファという語を使用しています。 |
抽出 |
iostream から入力データを取り出す操作を抽出といいます。 |
fstream |
ファイル用に特殊化された入出力ストリームです。特に courier フォントで印刷されている場合は、iostream クラスから派生した fstream クラスを指します。 |
挿入 |
iostream に出力データを送り込む操作を挿入といいます。 |
iostream |
一般には、入力ストリームまたは出力ストリームです。 |
iostream ライブラリ |
ファイル iostream.h、fstream.h、strstream.h、iomanip.h、ライブラリ stdiostream.h をインクルードすることにより使用できるライブラリです。iostream はオブジェクト指向のライブラリですので、ユーザーが必要に応じて拡張することができます。そのため、iostream ライブラリを使用して実行できるすべての機能があらかじめ定義されているわけではありません。 |
ストリーム |
一般に、iostream、fstream、strstream、またはユーザー定義のストリームをいいます。 |
streambuf |
文字シーケンスの入ったバッファで、put ポインタまたは get ポインタ (またはその両方) を持ちます。 courier フォントで印刷されている場合は、streambuf という特定のクラスを意味します。その他のフォントで印刷されている場合は一般に streambuf クラスのオブジェクト、または streambuf の派生クラスを意味します。ストリームオブジェクトは必ず、streambuf から派生した型のオブジェクト (またはそのオブジェクトへのポインタ) を持っています。 |
strstream |
文字型配列処理用に特殊化した iostream です。courier フォントで印刷されている場合は、 strstream という特定のクラスを意味します。 |