ファイルストリーム
std::ios::out 書き込みモード (ofstreamのデフォルト) 書き込み時に、以前の内容は破棄される std::ios::in 読み取りモード (ifstreamのデフォルト) std::ios::app 追記モード 常にファイルの末尾に書き込み (append) std::ios::ate オープンと同時にファイル末尾に移動 オープン後に任意の位置に移動可能 std::ios::inと同時に指定する(後述) (at end) std::ios::trunc 上書きモード ファイルを開いた時点で以前の内容が破棄される (truncate) std::ios::binary バイナリモード
これらは std::ios 名前空間に存在します。 ファイルの読み書き動作はfopen関数も参考にしてください。
app と ate は似ていますが異なります。 app はファイル位置を末尾より手前に移動させても書き込みは常に末尾に行われます。 ate はファイル位置を移動させた位置に書き込みが可能です。
ate は単独指定では期待通りの動作にならないので in と組み合わせて使用します。 これについては後述します。
ファイル位置変更(シーク)ファイルの現在の位置の変更(シーク)には seekp メンバ関数を使用します。 これはC言語でのfseek関数と同様の働きをします。
std::ios::beg (std::ios_base::beg) ファイルの先頭SEEK_SET(fseek関数)と同等 std::ios::cur (std::ios_base::cur) ファイルの現在位置SEEK_CURと同等 std::ios::end (std::ios_base::end) ファイルの末尾SEEK_ENDと同等
ファイル位置取得ファイルの現在位置の取得は tellp メンバ関数を使用します。 これはC言語の ftell 関数に対応します。
この関数に引数はなく、戻り値は std::streampos 型です。 これはストリーム上の位置を表す値です。 内部的にはクラスで実装されていますが、 std::streampos 型同士を+や-で演算したり、 std::cout で数値を出力したりできます。
サンプルコードファイルのオープンモードと seekp 関数、 tellp 関数を使ったサンプルコードです。
#include #include //現在のファイル位置を表示する関数 std::streampos tellFilePoint(std::ofstream& ofs) < std::streampos pos = ofs.tellp(); std::cout int main() < const char* fileName = "C:\\test.txt"; < //ファイルを新規に作成 std::ofstream ofs(fileName); if (!ofs) < std::cout //適当に書き込んでファイルを閉じる ofs < //上で作ったファイルを開いて終端に移動 std::ofstream ofs(fileName, std::ios::in | std::ios::ate); if (!ofs) < std::cout //現在のファイル位置を取得&表示 std::streampos pos = tellFilePoint(ofs); ofs std::cin.get(); > 現在のファイル位置: 3 現在のファイル位置: 8 現在のファイル位置: 3 現在のファイル位置: 6ファイルのオープンモードは | (ビットOR演算子)を使用して複数同時に指定可能です。 ate はファイルのオープンと同時に末尾に移動するモードですが、 ofstream は書き込み専用のファイルストリームなので、単独で指定してもファイルの末尾が検出できないようです。 そのため、 in と同時に指定することでファイルの末尾まで位置を移動させます。 ( app は単独指定でも動作します) ただし、 ofstream にはファイル読み取り関数がないので、 in を指定してもファイル内容を読み取って表示させるようなことはできません。
入力ストリーム
ファイルの読み取りには std::ifstream を使用します。
#include #include #include int main() < const char *fileName = "C:\\test.txt";> data; std::cout Thisifstream も、使い方は std::cin と同様に >> で変数に格納できます。 実行結果を見るとファイルの内容である「This is a pen.」のうち「This」までしか取得できていませんが、これも std::cin と同様で空白文字が入力の区切り文字と判断されるからです。
改行までを読み込む改行文字までを読み込みたい場合は std::getline 関数を使用します。 この関数は ヘッダファイルのインクルードが必要です。
std::string data; std::ifstream ifs("a.txt"); std::getline(ifs, data); //一行読み取る第一引数は読み取りを行うストリームを指定します。 この関数はC言語のfgets関数と同じように、入力ストリームを指定して文字列を取得できます。 ここに std::cin を指定すると標準入力から文字列を読み取ります。
std::getline 関数はstringクラスの関連関数として定義されています。 ( ヘッダ内に宣言がある) ifstream にも getline メンバ関数が存在しますが、こちらは格納先にstringクラスを指定できず、char型の配列を指定する必要があります。 C++ではstring型を使った方が便利で安全なので、特別な理由がない限りは std::getline 関数を使用したほうが良いでしょう。
ファイル終端チェック eof関数eof 関数は、ファイルが終端に達した場合に真を返します。 これを利用してファイルの内容をすべて読み込むサンプルです。
#include #include #include int main() < const char* fileName = "C:\\test.txt";> std::couteof 関数が偽を返すまで、while文でファイルの読み取りを繰り返します。 getline 関数は改行文字までを読み取りますが、改行文字自体は変数に格納しないので、読み取った文字列の末尾に改行を足しておきます。
good() 通常状態(エラーなし) eof() ファイルの終端 fail() 読み取りの失敗 bad() その他のエラー
ストリーム自体をチェックストリーム自身を条件判定すると、 fail 状態または bad 状態のときに偽を返します。 これを利用してファイル終端を検出することもできます。 (正確には、読み取りエラー発生の判定) >> 演算子による入力や getline 関数などは戻り値としてストリーム自身を返すので、以下のような記述が可能です。
std::ifstream ifs(fileName); std::string buf; //真を返す場合は読み取り成功 while (std::getline(ifs, buf)) 状態のクリアeof などの状態になったストリームを通常状態( good )に戻すには clear 関数を使用します。
std::ifstream ifs(fileName); std::string buf; //真を返す場合は読み取り成功 while (std::getline(ifs, buf)) < data += buf + "\n"; >//ここに到達した時点でストリームはgood以外の状態 //状態をgoodに戻す ifs.clear(); //ファイル位置を先頭に戻す ifs.seekg(0, std::ios::beg);一度 good 以外の状態になったストリームに対してはこの関数で good 状態に戻しておかないと、シークなども行えなくなるので注意してください。
ファイル位置の設定と取得入力ストリームでファイル位置を変更する場合は seekg 関数を使用します。 現在のファイル位置は tellg 関数で取得できます。
出力ストリームの場合はseekp、tellp、入力ストリームの場合はseekg、tellgと微妙に名前が異なります。 (put(書き込み)のp、get(読み取り)のgだそうです)
入出力ストリーム
fstream は、入力と出力を同時に行うことができます。 これらは ofstream と ifstream を合体させたようなもので、使い方も同じです。 双方向ストリームとも言います。
#include #include #include int main() < const char* fileName = "R:\\test.txt"; //テスト用のファイル生成 < std::ofstream ofs(fileName); if (!ofs) < std::cout ofs //この時点でファイル生成完了 std::string data; < std::fstream fs(fileName); if (!fs) < std::cout //ファイル読み込み std::string buf; size_t match; while (std::getline(fs, buf)) < match = buf.find("pen"); if (match != std::string::npos) buf.replace(match, 3, "drink"); //3は"pen"の文字数 data += buf + "\n"; >fs.clear(); //eofフラグをクリア fs.seekp(std::ios::beg); //ファイル位置を先頭にセット //ファイル書き込み fs std::cout This is a drink. That is a book. This is a car. That is a drink.このサンプルコードでは、文字列中に「pen」という文字が登場したら「drink」に置き換える処理をしています。 stringクラスの find 関数は文字列が登場する位置を返します。 見つからなかった場合は std::string::npos を返しるので、それ以外の場合に置き換えを行います。 (C++の文字列2を参照)
一度ファイル終端まで達すると、 seekp などのファイル位置移動関数が効かなくなるので clear 関数を使用して状態をリセットします。 その上で、ファイル位置を先頭に戻してからファイルを上書きしています。
バイナリデータ
ファイルストリームでバイナリを扱うには、オープンモードに std::ios::binary を指定します。
シフト演算子( > )によるデータの流し込みはそれに対応しているデータ型やクラスにしか使えないので、 read 関数と write 関数を使用します。
streamsize というデータ型はストリームのサイズを表す符号つき整数型です。 ( std 名前空間)
#include #include #include #include int main() < auto a = sizeof(0); const char* fileName = "R:\\test.bin"; //書き込むデータのサイズ(要素数) size_t length; //ファイルに書き込み < //書き込むデータの作成 std::vectorvec_out; for (int i = 0; i < 5; i++) vec_out.push_back(i * 2); length = vec_out.size(); //ファイル書き込み std::ofstream ofs(fileName, std::ios::binary); if (!ofs) < std::cout //ファイル先頭に要素数を書き込み ofs.write(reinterpret_cast(&length), sizeof(size_t)); ofs.write(reinterpret_cast(&vec_out[0]), sizeof(int) * static_cast(length)); > //データ受け取り用 std::vector vec_in; //ファイルからデータ復元 < std::ifstream ifs(fileName, std::ios::binary); if (!ifs) < std::cout //ファイル先頭から要素数を読み取り容量確保 ifs.read(reinterpret_cast(&length), sizeof(size_t)); vec_in.resize(length); ifs.read(reinterpret_cast(&vec_in[0]), sizeof(int) * static_cast(length)); > for (int i = 0; i 0 2 4 6 8一度データをファイルに保存し、ファイルからデータが復元できていることを確認するサンプルコードです。 read 関数も write 関数も、char*型(const char*型)しか扱うことができないので、それ以外のデータ型の場合はキャストが必要です。