目次
低レイヤーのプログラミングとOS開発が趣味。C言語を使っています。
テキストモード
読み込み
input.txt
1 10 100
2 20 200
main.cpp
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib> // EXIT_FAILURE のため
using namespace std;
int main() {
fstream fs;
fs.open("input.txt", ios::in);
if(! fs.is_open()) {
// exit だけでなく return の戻り値としても使用できる。
return EXIT_FAILURE;
}
// 文字列として一行読み込む
string str;
getline(fs, str); //← cin ではなく今回は fs
cout << str << endl; //=> 1 10 100
// cin のように一行読み込む
int l,m,n;
fs >> l >> m >> n;
cout << "l: " << l << ", "
"m: " << m << ", "
"n: " << n << endl; //=> l: 2, m: 20, n: 200
fs.close(); // デストラクタでも閉じてくれますが、明示的に閉じる習慣を。
return 0;
}
書き込み
main.cpp (UTF-8)
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib> // EXIT_FAILURE のため
using namespace std;
int main() {
fstream fs;
fs.open("output.txt", ios::out);
if(! fs.is_open()) {
return EXIT_FAILURE;
}
// シンプルに書き出す
fs << "改行含める" << endl; // 改行。そして書き出す
fs << "改行含めない" << flush; // 書き出すだけ
// flush() メソッドで書き出す
fs << "改行含めない";
fs.flush(); // cout.flush() というのも実はあり、同様の効果です
// close() で暗黙的に書き出す (閉じるときにバッファをすべて書き出してくれる)
fs << "改行含めない";
fs.close();
return 0;
}
output.txt (UTF-8)
改行含める
改行含めない改行含めない改行含めない ← 改行なしでEOF
フラグ
上記サンプルコード内の
- 入力: ios::in
- 出力: ios::out
という基本のフラグにオプションとなるフラグを追加することで、入出力に関する振舞を変更できます。ちなみに ios::out ではファイルが存在していなくても is_open() は true になりますが ios::in では false になります。
- 追記(append): ios::app
- ファイルが存在していれば破棄(truncate): ios::trunc
- ファイルポインタを末尾(at e(nd))に移動: ios::ate
- バイナリモードで開く: ios::binary
組み合わせの例をいくつか示します。
ios::in | ios::out
読み込みと書き込みができる。既存のファイル内容は書き込んだ時点で破棄される。
ios::in | ios::out | ios::app
読み込みと書き込みができる。書き込んだ内容は既存のファイルがあれば追記される。なければ新規作成される。
ios::in | ios::app
エラーです。読み込みのみで追記というオプションは意味をなしません。
ios::out | ios::app
書き込みができる。書き込んだ内容は既存のファイルがあれば追記される。なければ新規作成される。
ios::in | ios::out | ios::trunc
読み込みと書き込みができる。既存のファイル内容は開いた時点で破棄される。
ios::in | ios::trunc
エラーです。何も読み込めないファイルストリームを作成しても無意味です。
ios::out | ios::trunc
書き込みができる。既存のファイル内容は開いた時点で破棄される (開いた時点で破棄せずとも out すれば勝手に上書きされるため ios::out だけの場合と実質的には同じです。冗長な表現です)
バイナリモード
テキストモードの場合、「fs << "\n" << flush」が例えば Windows だと "\r\n" になりました。読み込みの場合にも "\r\n" は改行として扱われました。一方、バイナリモードの場合、「fs << "\n" << flush」はOSによらず "\n" のまま出力されます。読み込みについても OS によらず "\n" として読み込まれます。
実は、バイナリモードとテキストモードの違いはこの「改行コードの扱い」だけです。ios::binary を忘れに注意してください。改行コードの点でしか違いがないため不具合が発生する箇所が限定的であり気づきにくいです。
書き込み
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
int main() {
fstream fs;
fs.open("output.bin", ios::out | ios::binary);
if(! fs.is_open()) {
return EXIT_FAILURE;
}
int n = 0x41424344; // 16進数 (4バイト (4ビット: 2進数1111 == 16進数F なので、32ビットで4バイト))
// int n = 010120441504; // 8進数
// int n = 1094861636; // 10進数
// int n = 'A' * (16*16*16*16*16*16) + 'B' * (16*16*16*16) + 'C' * (16*16) + 'D';
// ↑0x41, 0x42, 0x43, 0x44 は 'A','B','C','D' のアスキーコード (1バイト x 4)
// int型 sizeof n バイト (通常4バイト) の領域の先頭
// アドレス &n を write の引数 const char* にキャスト
fs.write((const char*)&n, sizeof n);
//=> cat output.bin (↓endian: バイトオーダの種類を表す言葉
// バイトオーダはCPUに依存して決定されることがほとんどです)
// - リトルエンディアン "DCBA"
// (反転します)
// - オフセット(アドレス先頭&nからのバイト差) 0: 0x44
// - オフセット 1: 0x43
// - オフセット 2: 0x42
// - オフセット 3: 0x41
// - ビッグエンディアン "ABCD"
// - オフセット 0: 0x41
// - オフセット 1: 0x42
// - オフセット 2: 0x43
// - オフセット 3: 0x44
// ↑ファイルとメモリは本質的に同じ。binaryモードはメモリ内容をダンプしている
// イメージです。メモリ上でもバイトオーダは等しくなります。
fs.close();
return 0;
}
読み込み
input.bin
DCBA ← 改行なしでEOF
main.cpp
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
int main() {
fstream fs;
fs.open("input.bin", ios::in | ios::binary);
if(! fs.is_open()) {
return EXIT_FAILURE;
}
int n;
fs.read((char*)&n, sizeof n); // 0x41424344
cout << n << endl; //=> 1094861636 (sizeof n == 4 かつリトルエンディアンの場合)
fs.close();
return 0;
}
連続的な読み込みとエラー処理
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <cstdio> // printf, remove を使用するため
using namespace std;
const char SRC_FILE[] = "input.bin";
const char DST_FILE[] = "output.bin";
const int BUF_SIZE = 16; // バイト
int main() {
fstream src, dst;
// ファイルを開く
src.open(SRC_FILE, ios::in | ios::binary);
if(! src.is_open()) {
return EXIT_FAILURE;
}
dst.open(DST_FILE, ios::out | ios::binary);
if(! dst.is_open()) {
src.close();
return EXIT_FAILURE;
}
bool error = false;
do {
char buf[BUF_SIZE];
// 読み込み
src.read(buf, sizeof buf); // BUF_SIZE バイトずつ
if(src.fail() && ! src.eof()) { // ! src && ! src.eof としても同じです
error = true;
break;
}
// 内容を16進ダンプ
for (int i = 0, size = src.gcount(); i < size; ++i) { // get count
printf("%02X ", (unsigned char)buf[i]); // 2桁の16進数大文字表示 (小文字表示は %x)
// ↑char型のままでは負の値として扱われる可能性がある
}
cout << endl;
// 書き込み
dst.write(buf, src.gcount());
if(dst.fail()) { // ! dst としても同じです
error = true;
break;
}
} while (! src.eof()); // eof: ファイル終端にファイルポインタがあってもfalseです。
// 終端を越えようとしていればtrueです。そのため、input.bin が空であっても最初はfalseです
// よって、do-while であっても while であっても表示結果は同じで最低1ループは実行されます。
// ここでは現実をそのまま直感的に表現したdo-whileを採用しました。
src.close();
dst.close();
if (error) {
remove(DST_FILE); // ファイル削除 (cstdioより)
return EXIT_FAILURE;
}
return 0;
}
ファイルポインタの移動
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
int main() {
fstream fs;
fs.open("input.bin", ios::in | ios::binary);
if(! fs.is_open()) {
return EXIT_FAILURE;
}
fs.clear(); // fs.fail() が true の状態が発生するとファイル操作できなくなります。これでは
// ファイル終端に到達した場合などに不都合であるため seekg() などの直前では毎回
// clear() でロック解除するとよいです。今回の例では open 直後なのでなくてもよいですが。
// 絶対的な移動
fs.seekg(0); // 'g': get 読み込み (ファイル先頭 0バイト目に移動)
fs.seekp(0); // 'p': put 書き込み (実際には seekg と seekp は同じ関数)
// 相対的な移動
// - 現在の位置(current): ios::cur
// - ファイル先頭(beginning): ios::beg
// - ファイル末尾(end) + 1バイト目: ios::end
// (何も読み込むデータがない位置。追記時に書き込む位置)
fs.seekg(-10, ios::end);
// 現在の位置の取得
streampos pos = fs.tellg(); // tellp() としても同じです。seekg と seekp の関係と同じです。
// 注意点
// streampos 型は int 型であるとは限らないため
// fs.seekg(pos + 10); としてははならず
fs.seekg(pos);
fs.seekg(10, ios::cur); // としてください。
// 環境によってはどちらもコンパイルできてしまうので厄介です。
return 0;
}
記事の執筆者にステッカーを贈る
有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。
さらに詳しく →Feedbacks
ログインするとコメントを投稿できます。
関連記事
- ダウンキャスト (C++をもう一度)実行時型情報 RTTI #include <iostream> #include <typeinfo> using namespace std; class MyClass { public: virtual ~MyClass() {} // typeid で正しい RTTI // (RunTime Type Information; 実行時型情報) ...
- 競技プログラミングの基本処理チートシート (C++)限られた時間の中で問題を解くために必要となる、競技プログラミングにおける基本的な処理のチートシートです。競プロにおけるメジャー言語 C++ を利用します。その際 C++11 の機能は利用せず C++03 の機能の範囲内で記述します。 頻度高く定期的に開催されるコンテスト AtCoder Codeforces main.cpp #include <iostream>
- 構造体と列挙体 (C++をもう一度)構造体 #include <iostream> using namespace std; struct MyStruct { char charval; int intval; }; void Show(MyStruct* obj) { cout << obj->intval << endl; } int main() { ...
- Valgrind による C/C++ メモリリーク検出JVM メモリリークでは JDK の jstat や jmap で原因を調査できます。C/C++ では valgrind の Memcheck ツールが利用できます。valgrind には複数のツールが含まれており既定のツールが Memcheck です。他のツールを利用する場合は --tool オプションで指定します。 [簡単な利用例](h
- クラスの基本/初期化 (C++をもう一度)構造体のように初期化する (非推奨) #include <iostream> using namespace std; const int MAX_STR = 16; class MyClass { public: int m_integer; char m_str[MAX_STR + 1]; void Show(); }; void MyClass::Show...