HOWTO · C++

C++ でファイルに書き込む方法

この記事では、C++ でファイルに書き込む方法を説明します。ファイルストリームやさまざまなファイルモードについて学び、C++ プログラムでファイル操作を効果的に管理するための明確な例を確認できます。データを追加したい場合も、既存の内容を上書きしたい場合も、このガイドが役立ちます。

このページの内容

通常のテキストを書き込むには、std::ofstream と挿入演算子 (<<) を使います。末尾にデータを追加するには std::ios::app、既存の内容を置き換えるには std::ios::trunc を指定します。文字数またはバイト数が決まっているデータにはストリームのメンバー関数 write を使えます。C形式の代替手段は fwrite です。

C++のファイルストリームを理解する

ファイルストリームを使うと、C++プログラムから名前を指定したファイルにアクセスできます。<fstream> をインクルードし、出力には std::ofstream を作成します。同じストリームで入力と出力の両方を行う場合は std::fstream を使います。挿入演算子は書式付きテキストの出力に便利で、文字列に改行を含めるとファイル内で次の行が始まります。パスが正しく見えてもアクセスできない場合や、ストレージを利用できない場合があるため、ファイルを開いた後と書き込み後にストリームの状態を確認してください。

次の例を実行します。

#include <fstream>
#include <iostream>

int main() {
    std::ofstream file("example.txt");
    if (!file) {
        std::cerr << "Could not open example.txt\n";
        return 1;
    }

    file << "Hello, C++!\n";
    file << "File output is ready.\n";
    file.close();
    if (!file) {
        std::cerr << "Could not finish writing example.txt\n";
        return 1;
    }

    std::cout << "Wrote example.txt\n";
    return 0;
}
Wrote example.txt

メッセージはコンソールに表示され、file を使って書き込んだ2行は example.txt に保存されます。内容を確認するには、そのファイルをテキストエディターで開いてください。出力専用の std::ofstream は、親ディレクトリが存在すればファイルを作成し、既存のファイルを開くと通常は内容を消去します。

ファイルを異なるモードで書き込む

ファイルを開くモードによって、既存データの扱いが変わります。std::ios::app を指定すると、書き込みのたびにファイル末尾へ移動します。ログや記録のようにデータを蓄積したい場合に便利です。パスと権限に問題がなければ、出力用に開く際にファイルが存在しない場合でも作成できます。次の単独で実行できる例は追記モードで1行を書き込みます。事前に example.txt を作成しておく必要はありません。

#include <fstream>
#include <iostream>

int main() {
    std::ofstream file("example.txt", std::ios::app);
    if (!file) {
        std::cerr << "Could not open example.txt\n";
        return 1;
    }

    file << "A new log entry\n";
    file.close();
    if (!file) {
        std::cerr << "Could not finish writing example.txt\n";
        return 1;
    }
    return 0;
}

実行結果では、処理は成功し、コンソールには何も出力されません。コードは A new log entry の後に改行を書き込みます。ファイルにすでにデータがある場合は、その末尾に新しい行が追加されます。

ios::truncでファイルを書き込み、上書きする

std::ios::trunc を指定してファイルを開くと、以前の内容は破棄されます。std::ofstream を出力用に開き、std::ios::app を指定しない場合も、通常は同じように既存の内容が切り詰められます。この動作を明示したい場合は std::ios::trunc を指定してください。以前のデータを残す必要がある場合は、どちらの方法も使わないでください。

#include <fstream>
#include <iostream>

int main() {
    std::ofstream file("example.txt", std::ios::trunc);
    if (!file) {
        std::cerr << "Could not open example.txt\n";
        return 1;
    }

    file << "Current report\n";
    file.close();
    if (!file) {
        std::cerr << "Could not finish writing example.txt\n";
        return 1;
    }
    return 0;
}

実行結果では、処理は成功し、コンソールには何も出力されません。コードは example.txt に Current report と改行だけを書き込み、以前の内容があれば置き換えます。

fstream::writeでファイルに書き込む

データがすでに文字バッファにあり、その長さが分かっている場合は、ストリームのメンバー関数 write を使います。この関数には文字へのポインターと、書き込む文字数を渡します。終端文字や改行は自動では追加されません。文字数にはストリームサイズを指定するため、data.size() を渡すと、末尾のヌル文字を含めずに文字列が保持する文字を出力できます。書き込み失敗を検出するには、呼び出し後にストリームの状態を確認してください。インターフェースについては、cppreferenceのbasic_ostream::writeのドキュメントを参照してください。

#include <fstream>
#include <iostream>
#include <string>

int main() {
    const std::string data = "C++ buffer data\n";
    std::fstream file("buffer.txt", std::ios::out | std::ios::trunc);
    if (!file) {
        std::cerr << "Could not open buffer.txt\n";
        return 1;
    }

    file.write(data.data(), static_cast<std::streamsize>(data.size()));
    if (!file) {
        std::cerr << "Could not write buffer.txt\n";
        return 1;
    }
    file.close();
    if (!file) {
        std::cerr << "Could not finish writing buffer.txt\n";
        return 1;
    }
    return 0;
}

実行結果では、処理は成功し、コンソールには何も出力されません。コードは data の文字を buffer.txt に書き込みます。改行は文字列に含まれていますが、末尾のヌル文字は書き込まれません。

文字単位の書き込みとバイト単位の書き込みは、データが書式化されるかどうかが異なります。挿入演算子は値をテキストに変換しますが、write と fwrite はバッファのバイトを書式化せずにコピーします。ただし、バイトをコピーするだけでは、ファイルにそのまま届く保証にはなりません。テキストモードで開いたストリームは、行末コードが異なるプラットフォームでは改行文字を変換する場合があります。たとえば Windows は出力時に \n を \r\n に変換します。正確なバイトが重要な場合は、std::ios::out | std::ios::binary | std::ios::trunc のようにストリームをバイナリモードで開くか、fopen に "w" の代わりに "wb" を渡して、変換が行われないようにします。

Cのfwrite関数でファイルに書き込む

C形式のファイル入出力では、fwrite にポインター、要素サイズ、要素数、FILE* を渡します。戻り値は書き込んだバイト数ではなく要素数です。ただし、各要素のサイズが1の場合はバイト数と一致します。戻り値を確認してからファイルを閉じ、fclose の戻り値も確認してください。バッファーされた書き込みのエラーは、ファイルを閉じるときに初めて発生する場合があります。

#include <cstdio>
#include <cstring>
#include <iostream>

int main() {
    const char data[] = "C file output\n";
    const std::size_t size = std::strlen(data);
    std::FILE* file = std::fopen("c-output.txt", "w");
    if (file == nullptr) {
        std::cerr << "Could not open c-output.txt\n";
        return 1;
    }

    const std::size_t written = std::fwrite(data, 1, size, file);
    const int close_status = std::fclose(file);
    if (written != size || close_status != 0) {
        std::cerr << "Could not finish writing c-output.txt\n";
        return 1;
    }
    return 0;
}

実行結果では、処理は成功し、コンソールには何も出力されません。コードは改行を含む data の文字を c-output.txt に書き込みます。"w" モードはファイルを作成し、既存の内容があれば切り詰めます。

まとめ

通常のテキストには std::ofstream と << を使います。既存の内容を残して末尾に追加するには std::ios::app、内容を置き換えるには std::ios::trunc を選びます。長さが決まっているバッファーには write を使い、C形式の入出力を扱う場合は fwrite を使います。ファイルを開いたときと書き込んだ後に状態を確認し、データがすべて書き込まれたかを確認する必要がある場合は、ファイルを閉じるときのエラーも確認してください。

よくある質問

  1. ファイルを開けたかどうかは、どう確認できますか? ストリームを直接 (if (!file)) 確認するか、開いた直後に is_open() を呼び出します。失敗した場合は、書き込む前に対処してください。

  2. 既存の内容を置き換えずに追記するには、どうすればよいですか? 出力ストリームを std::ios::app を指定して開きます。各書き込みはファイルの末尾に行われます。

  3. C++は存在しないファイルを作成しますか? 出力ストリームや、モード "w" を指定した fopen は、親ディレクトリが存在し、プロセスに必要な権限があればファイルを作成できます。親ディレクトリがない場合や権限が不足している場合は、ファイルを開けません。

  4. 複数行を書き込むには、どうすればよいですか? << で改行文字を挿入するか、write に渡すバッファーに改行を含めます。write も fwrite も、改行を自動では追加しません。

  5. ファイルを閉じる必要はありますか? ファイルストリームは破棄時に閉じられますが、close() を明示的に呼び出すと、処理の完了箇所が分かりやすくなります。書き込みが完了したことを確認する必要がある場合は、閉じた後にストリームの状態を確認してください。Cの FILE* では fclose を呼び出し、その戻り値を確認します。