ファイル I/O(fstream)
『C++』のファイル入出力は、標準ライブラリの <fstream> ヘッダが提供する ifstream(入力)・ofstream(出力)・fstream(入出力両用)の3つのクラスを使って行います。これらは iostream と同じ << / >> 演算子で操作でき、ファイルを閉じる処理はデストラクタが自動的に担当するため、スコープを抜けると安全にクローズされます。
構文
// ========================================
// ファイル入出力の基本構文
// ========================================
#include <fstream>
#include <string>
// --- ファイルへの書き込み(ofstream)---
// ファイルが存在しなければ新規作成、存在すれば上書きします
std::ofstream ofs("output.txt");
if (ofs.is_open()) { // 正常にオープンできたか確認します
ofs << "テキストを書き込みます" << std::endl;
}
// ofs のスコープを抜けると自動的に close() されます
// --- ファイルからの読み込み(ifstream)---
std::ifstream ifs("output.txt");
std::string line;
while (std::getline(ifs, line)) { // 1行ずつ読み込みます
// line に1行分の内容が入ります
}
// --- 入出力両用(fstream)---
// std::ios::in | std::ios::out でモードを指定します
std::fstream fs("data.txt", std::ios::in | std::ios::out);
// --- 追記モード(ofstream + app フラグ)---
// 既存内容を残して末尾に追記します
std::ofstream ofs_app("log.txt", std::ios::app);
ofs_app << "追記行" << std::endl;
主要クラス・フラグ一覧
| クラス / フラグ | 概要 |
|---|---|
| ifstream | ファイルからの読み込み専用ストリームです。コンストラクタにパスを渡してオープンします。 |
| ofstream | ファイルへの書き込み専用ストリームです。デフォルトでファイルを上書き作成します。 |
| fstream | 読み書き両用のストリームです。モードフラグで動作を制御します。 |
| is_open() | ファイルが正常にオープンされているかを bool で返します。エラーチェックに使用します。 |
| getline(stream, str) | ストリームから1行読み込み、str に格納します。改行文字は含まれません。 |
| close() | ストリームを明示的に閉じます。スコープを抜けると自動的に呼ばれます。 |
| std::ios::app | 追記モードです。既存ファイルの末尾にデータを追加します。 |
| std::ios::trunc | 切り捨てモードです。オープン時にファイルの内容を空にします(ofstream のデフォルト)。 |
| std::ios::binary | バイナリモードです。改行コードの変換を行わずにデータを読み書きします。 |
| eof() / fail() / bad() | ストリームの状態を確認するメンバ関数です。読み込みループの終了判定などに使います。 |
サンプルコード
jujutsu_file_io.cpp
// ========================================
// jujutsu_file_io.cpp
// 呪術廻戦のキャラクター情報をファイルに書き込み、
// 読み込んで表示するサンプルです
// ========================================
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
// ----------------------------------------
// キャラクター情報を保持する構造体です
// ----------------------------------------
struct Sorcerer {
std::string name; // 術師名
int grade; // 等級(数字が小さいほど強い)
std::string ability; // 術式・特技
};
// ----------------------------------------
// CSV 形式でファイルに書き込む関数です
// ----------------------------------------
void writeSorcerers(const std::string& filename, const std::vector<Sorcerer>& sorcerers) {
std::ofstream ofs(filename);
if (!ofs.is_open()) {
std::cerr << "エラー: ファイルをオープンできませんでした: " << filename << std::endl;
return;
}
// ヘッダ行を書き込みます
ofs << "name,grade,ability" << std::endl;
// 各キャラクターを1行ずつ CSV 形式で書き込みます
for (int i = 0; i < (int)sorcerers.size(); i++) {
ofs << sorcerers[i].name << ","
<< sorcerers[i].grade << ","
<< sorcerers[i].ability << std::endl;
}
std::cout << "[書き込み完了] " << filename << " に "
<< sorcerers.size() << " 件書き込みました。" << std::endl;
// ofstream はここでスコープを抜けてデストラクタが close() を呼びます
}
// ----------------------------------------
// CSV ファイルを読み込んで Sorcerer の vector を返す関数です
// ----------------------------------------
std::vector<Sorcerer> readSorcerers(const std::string& filename) {
std::vector<Sorcerer> result;
std::ifstream ifs(filename);
if (!ifs.is_open()) {
std::cerr << "エラー: ファイルをオープンできませんでした: " << filename << std::endl;
return result;
}
std::string line;
// 1行目はヘッダなのでスキップします
std::getline(ifs, line);
// 2行目以降をデータとして読み込みます
while (std::getline(ifs, line)) {
if (line.empty()) { continue; }
std::istringstream ss(line);
std::string token;
Sorcerer s;
// カンマで分割して各フィールドに代入します
std::getline(ss, token, ',');
s.name = token;
std::getline(ss, token, ',');
s.grade = std::stoi(token); // 文字列を整数に変換します
std::getline(ss, token, ',');
s.ability = token;
result.push_back(s);
}
std::cout << "[読み込み完了] " << filename << " から "
<< result.size() << " 件読み込みました。" << std::endl;
return result;
}
// ----------------------------------------
// ログを追記モードで記録する関数です
// ----------------------------------------
void appendLog(const std::string& logFile, const std::string& message) {
// std::ios::app を指定すると既存ファイルの末尾に追記します
std::ofstream ofs(logFile, std::ios::app);
if (!ofs.is_open()) {
std::cerr << "エラー: ログファイルをオープンできませんでした。" << std::endl;
return;
}
ofs << message << std::endl;
}
// ----------------------------------------
// メイン関数
// ----------------------------------------
int main() {
const std::string dataFile = "sorcerers.csv";
const std::string logFile = "battle_log.txt";
// ========================================
// Step 1: キャラクターデータをファイルに書き込みます
// ========================================
std::vector<Sorcerer> team;
team.push_back({"虎杖悠仁", 1, "茈(むらさき)・発勁"});
team.push_back({"伏黒恵", 2, "十種影法術・渕源の獄"});
team.push_back({"釘崎野薔薇", 3, "芻霊呪法・共鳴り"});
team.push_back({"五条悟", 0, "無下限呪術・術式順転「蒼」"});
team.push_back({"七海建人", 1, "十劃呪法・六分の一"});
writeSorcerers(dataFile, team);
// ========================================
// Step 2: ファイルから読み込んで表示します
// ========================================
std::cout << std::endl;
std::vector<Sorcerer> loaded = readSorcerers(dataFile);
std::cout << std::endl;
std::cout << "=== 呪術師データ一覧 ===" << std::endl;
std::cout << std::left; // 左揃えで出力します
for (int i = 0; i < (int)loaded.size(); i++) {
std::cout << " 名前: " << loaded[i].name
<< " 等級: ";
if (loaded[i].grade == 0) {
std::cout << "特級";
} else {
std::cout << loaded[i].grade << "級";
}
std::cout << " 術式: " << loaded[i].ability << std::endl;
}
// ========================================
// Step 3: 追記モードでバトルログを記録します
// ========================================
std::cout << std::endl;
appendLog(logFile, "--- バトル開始 ---");
appendLog(logFile, "虎杖悠仁 vs 真人: 虎杖が茈を発動した。");
appendLog(logFile, "五条悟 vs 漏瑚: 五条が蒼→赫を展開した。");
appendLog(logFile, "七海建人 vs 花御: 七海が十劃呪法を使用した。");
appendLog(logFile, "--- バトル終了 ---");
std::cout << "[追記完了] " << logFile << " にログを追記しました。" << std::endl;
// ========================================
// Step 4: ログファイルを読み込んで表示します
// ========================================
std::cout << std::endl;
std::cout << "=== バトルログ ===" << std::endl;
std::ifstream logIfs(logFile);
if (logIfs.is_open()) {
std::string logLine;
while (std::getline(logIfs, logLine)) {
std::cout << " " << logLine << std::endl;
}
}
return 0;
}
# コンパイルして実行します g++ -std=c++11 jujutsu_file_io.cpp -o jujutsu_file_io && ./jujutsu_file_io [書き込み完了] sorcerers.csv に 5 件書き込みました。 [読み込み完了] sorcerers.csv から 5 件読み込みました。 === 呪術師データ一覧 === 名前: 虎杖悠仁 等級: 1級 術式: 茈(むらさき)・発勁 名前: 伏黒恵 等級: 2級 術式: 十種影法術・渕源の獄 名前: 釘崎野薔薇 等級: 3級 術式: 芻霊呪法・共鳴り 名前: 五条悟 等級: 特級 術式: 無下限呪術・術式順転「蒼」 名前: 七海建人 等級: 1級 術式: 十劃呪法・六分の一 [追記完了] battle_log.txt にログを追記しました。 === バトルログ === --- バトル開始 --- 虎杖悠仁 vs 真人: 虎杖が茈を発動した。 五条悟 vs 漏瑚: 五条が蒼→赫を展開した。 七海建人 vs 花御: 七海が十劃呪法を使用した。 --- バトル終了 ---
よくあるミス: is_open() チェックを省略する
ファイルが存在しない・権限がないなどの理由でオープンに失敗した場合でも、is_open() チェックなしで読み書きを続けると何も書き込まれなかったり、空のデータを読んだりして原因不明のバグになります。
// NG: is_open() チェックなしで書き込む
std::ofstream ofs("/root/restricted.txt");
ofs << "五条悟のメモ" << std::endl; // オープン失敗時は何も起きない(エラーも出ない)
OK: オープン後に必ず is_open() で確認します。
// OK: is_open() で確認してからアクセスする
std::ofstream ofs("memo.txt");
if (!ofs.is_open()) {
std::cerr << "エラー: ファイルをオープンできませんでした。" << std::endl;
return 1;
}
ofs << "五条悟のメモ" << std::endl;
g++ -std=c++11 file_open_check.cpp -o file_open_check ./file_open_check エラー: ファイルをオープンできませんでした。
よくあるミス: バイナリファイルをテキストモードで開く
Windows 環境ではテキストモードで開いたファイルの \n(LF)が \r\n(CRLF)に変換されます。画像や音声などのバイナリファイルをテキストモードで開くとデータが破壊されます。
// NG: バイナリファイルをテキストモードで開く(Windows で壊れる)
std::ofstream ofs("image.png");
ofs.write(binaryData, dataSize); // \n が \r\n に変換されてデータが破損する
OK: std::ios::binary フラグを追加します。
// OK: バイナリモードで開く
std::ofstream ofs("image.png", std::ios::binary);
ofs.write(binaryData, dataSize); // 変換なし、正確に書き込まれる
g++ -std=c++11 binary_write.cpp -o binary_write ./binary_write バイナリファイルの書き込み完了: 1024 バイト
概要
『C++』のファイル入出力は <fstream> ヘッダで提供される ifstream・ofstream・fstream を使って行います。これらは RAII(Resource Acquisition Is Initialization)の仕組みに従っており、オブジェクトがスコープを抜けると自動的にファイルが閉じられるため、閉じ忘れによるリソースリークを防げます。ファイルをオープンした後は is_open() でエラーチェックを行うのがよく使われるパターンです。テキストモードでは getline() を使った行単位の読み込みが安全で一般的です。追記書き込みが必要な場合は std::ios::app フラグを指定します。画像や音声などのバイナリファイルを扱う際は std::ios::binary フラグを追加することで、OS による改行コード変換を抑制できます。クロスリファレンス: string / クラス
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。