C/C++におけるバイナリファイルの読み書き入門:write・readメソッドの使い方
C++では、テキスト形式ではなくバイナリ形式でファイルを扱うことで、構造体などのデータを効率的かつ高速に保存・復元できます。本記事では、ofstream/ifstreamを使ったバイナリファイルの書き込みと読み取りの基本を、構文・アルゴリズム・サンプルコード付きで解説します。
バイナリファイルへの書き込み
C++でバイナリファイルにデータを書き込むには、writeメソッドを使用します。writeメソッドは、「putポインタ」の現在位置から、指定したバイト数のデータをストリームへ書き込みます。
- putポインタがファイル末尾にある場合 → ファイルは自動的に拡張されます。
- putポインタがファイルの中間を指している場合 → 既存のデータが新しいデータで上書きされます。
なお、書き込み中に何らかのエラーが発生すると、ストリームはエラー状態に設定されます。
writeメソッドの構文
ostream& write(const char*, int);
バイナリファイルの読み取り
C++でバイナリファイルを読み込むには、readメソッドを使用します。readメソッドは、指定されたストリームから指定バイト数のデータを取り出し、第1引数が指すメモリ領域に格納します。
読み込み中にエラーが発生すると、ストリームはエラー状態となり、以降のすべての読み込み操作が失敗します。このため、エラー処理を適切に行うことが重要です。
gcount()… 直前に読み込んだ文字数(バイト数)を取得できます。clear()… ストリームを再び使用可能な状態にリセットできます。
readメソッドの構文
istream& read(const char*, int);
処理の流れ(アルゴリズム)
Begin
Student構造体を作成し、変数を宣言する。
書き込み用にバイナリファイルを開く。
ファイルオープン時にエラーが発生していないか確認する。
変数をデータで初期化する。
ファイルが正常に開けたら、writeメソッドでバイナリデータを書き込む。
書き込み用のファイルを閉じる。
読み取り用にバイナリファイルを開く。
ファイルオープン時にエラーが発生していないか確認する。
ファイルが正常に開けたら、readメソッドでバイナリデータを読み込む。
読み取り用のファイルを閉じる。
エラーが発生していないか確認する。
データを出力する。
End.
サンプルコード
以下は、Student構造体の配列をバイナリファイル「student.dat」に書き込み、その後同じファイルから読み戻して表示する完全なサンプルです。
#include<iostream>
#include<fstream>
using namespace std;
struct Student {
int roll_no;
string name;
};
int main() {
ofstream wf("student.dat", ios::out | ios::binary);
if(!wf) {
cout << "Cannot open file!" << endl;
return 1;
}
Student wstu[3];
wstu[0].roll_no = 1;
wstu[0].name = "Ram";
wstu[1].roll_no = 2;
wstu[1].name = "Shyam";
wstu[2].roll_no = 3;
wstu[2].name = "Madhu";
for(int i = 0; i < 3; i++)
wf.write((char *) &wstu[i], sizeof(Student));
wf.close();
if(!wf.good()) {
cout << "Error occurred at writing time!" << endl;
return 1;
}
ifstream rf("student.dat", ios::in | ios::binary);
if(!rf) {
cout << "Cannot open file!" << endl;
return 1;
}
Student rstu[3];
for(int i = 0; i < 3; i++)
rf.read((char *) &rstu[i], sizeof(Student));
rf.close();
if(!rf.good()) {
cout << "Error occurred at reading time!" << endl;
return 1;
}
cout<<"Student's Details:"<<endl;
for(int i=0; i < 3; i++) {
cout << "Roll No: " << rstu[i].roll_no << endl;
cout << "Name: " << rstu[i].name << endl;
cout << endl;
}
return 0;
}
補足: std::string のような内部にポインタを持つ型を含む構造体をそのままバイナリ書き込みすると、ポインタのアドレス値まで保存されてしまい、読み戻した際に正しく復元できない可能性があります。実務では、POD型(intやdoubleなど)や固定長の char 配列を使うことが推奨されます。
実行結果
Student's Details: Roll No: 1 Name: Ram Roll No: 2 Name: Shyam Roll No: 3 Name: Madhu
まとめ
バイナリファイル操作のポイントは次のとおりです。
- 書き込みには
ostream::write()、読み取りにはistream::read()を使う。 - ファイルを開く際は必ず
ios::binaryを指定する。 - オープン時・入出力後の両方でエラーチェックを行い、必要に応じて
gcount()やclear()を活用する。 - 使い終わったら
close()で確実にファイルを閉じる。
-
【C/C++】#ifdefによるOS判別マクロで、OSに依存しないコードを書く方法
OSに依存しないコードとは ここで紹介するのは、実行環境のOSがWindowsであってもLinuxやmacOSであっても動作し、それぞれのOSに応じた処理を自動的に切り替えられるC/C++プログラムの書き方です。例えば、「Windowsならdirコマンド、それ以外ならlsコマンドでファイル一覧を取得したい」といったケースで役立ちます。 OSを判別する定義済みマクロ 多くのC/C++コンパイラには、コンパイル時に実行対象のOSを検出するための定義済みマクロが用意されています。GCCコンパイラで利用できる主なマクロは以下の通りです。 _WIN32: 32ビット・64ビット両方のWindows
-
C/C++のlseek()でn番目ごとのバイトを読み取り、別のファイルに書き込む方法
このチュートリアルでは、C言語のシステムコール lseek() を使って、ファイルから n バイトおきにデータを読み出し、その内容を別のファイルへ書き込むプログラムについて解説します。処理の概要このプログラムでは、2つのテキストファイル(start.txt と end.txt)を用意します。目的は、1つ目のファイル(入力ファイル)から内容を読み込み、lseek() を使ってファイルポインタを移動させながら、n バイトごとに1バイトずつ抜き出し、2つ目のファイル(出力ファイル)へ書き込むことです。lseek() は、ファイルディスクリプタが指す読み書き位置(オフセット)を変更するためのシステムコ