C++ では、ファイル入出力のためのストリームが <fstream> ヘッダで提供されている。
主に使うクラスは次の3つである。
std::ifstreamファイル入力用std::ofstreamファイル出力用std::fstream入出力の両方に使える
出力
#include <fstream>
#include <iostream>
int main()
{
std::ofstream outf { "Sample.txt" };
if (!outf)
{
std::cerr << "Sample.txt を開けなかった\n";
return 1;
}
outf << "This is line 1\n";
outf << "This is line 2\n";
}std::ofstream は、デフォルトでは書き込み用にファイルを開く。
入力
#include <fstream>
#include <iostream>
#include <string>
int main()
{
std::ifstream inf { "Sample.txt" };
if (!inf)
{
std::cerr << "Sample.txt を開けなかった\n";
return 1;
}
std::string word {};
while (inf >> word)
std::cout << word << '\n';
}>> は空白文字で区切って読むため、単語ごとに読み込まれる。
1行ずつ読む
1行全体を読みたいときは、getline(C++) を使う。
#include <fstream>
#include <iostream>
#include <string>
int main()
{
std::ifstream inf { "Sample.txt" };
std::string line {};
while (std::getline(inf, line))
std::cout << line << '\n';
}close と flush
ファイルは、close() を明示的に呼んで閉じることもできる。
ただし、多くの場合はスコープを抜けるとデストラクタによって自動で閉じられる。
出力はバッファされることがあるため、すぐに書き込みを反映したいときは flush() や std::flush を使える。
また、std::endl は改行に加えてフラッシュも行う。
ファイルモード
ファイルを開くときには、モードを指定できる。
std::ios::in読み込みstd::ios::out書き込みstd::ios::app末尾への追加std::ios::binaryバイナリモードstd::ios::trunc既存内容を消して開く
たとえば追記したい場合は次のように書く。
std::ofstream outf { "Sample.txt", std::ios::app };open
コンストラクタで開かずに、あとから open() で開くこともできる。
std::ofstream outf {};
outf.open("Sample.txt");ランダムアクセス
ファイルストリームでは、ファイル位置を動かして任意の場所を読むこともできる。
ファイルストリームは、現在どこを読んでいるか、どこへ書いているかを表す位置情報を持つ。 これを動かすことで、特定の場所へ飛んで読み書きできる。
seekg()読み取り位置を動かすseekp()書き込み位置を動かすtellg()現在の読み取り位置を返すtellp()現在の書き込み位置を返す
オフセットの基準には次を使う。
std::ios::begファイル先頭からstd::ios::cur現在位置からstd::ios::endファイル末尾から
inf.seekg(0, std::ios::beg); // 先頭へ
inf.seekg(5, std::ios::beg); // 先頭から 5 バイト
inf.seekg(-2, std::ios::end); // 末尾の 2 バイト手前位置を変えて読む
#include <fstream>
#include <iostream>
#include <string>
int main()
{
std::ifstream inf{ "Sample.txt", std::ios::binary };
if (!inf)
return 1;
inf.seekg(5, std::ios::beg);
std::string str {};
std::getline(inf, str);
std::cout << str << '\n';
}tellg() / tellp()
現在位置を調べるには tellg() や tellp() を使う。
ファイルサイズを大まかに知りたいときにも使える。
std::ifstream inf{ "Sample.txt", std::ios::binary };
inf.seekg(0, std::ios::end);
std::streampos size { inf.tellg() };テキストファイルでの注意
テキストモードでは改行の表現が環境ごとに異なるため、途中位置への seekg() / seekp() は期待どおりにならないことがある。
ランダムアクセスを正確に行いたいなら、基本的にはバイナリモードの方が安全。
fstream で読み書き両方を行う
std::fstream は、1 つのファイルに対して読み書きの両方を行える。
std::fstream io{ "Sample.txt", std::ios::in | std::ios::out };ただし、読み取りのあとに書き込み、または書き込みのあとに読み取りへ切り替えるときは、間にファイル位置を変更する操作を挟む必要がある。
io.seekg(io.tellg(), std::ios::beg);位置変更を挟まずに読み書きを切り替えると、期待しない動作になることがある。
その他
is_open()ストリームが現在ファイルを開いているか調べるremove()ファイルを削除する
また、ポインタの値そのものはメモリアドレスなので、ファイルへ保存してあとで再利用する用途には向かない。 アドレスを書き出しても、次回実行時には同じ場所を指す保証がない。