HOWTO · C++

如何在 C++ 中写入文件

本文演示如何在 C++ 中写入文件。了解文件流和不同的文件模式,并通过清晰的示例有效地管理 C++ 程序中的文件操作。无论您需要追加数据还是覆盖现有内容,本指南都能帮到您。

本页内容

使用 std::ofstream 和插入运算符(<<)即可将普通文本写入文件。使用 std::ios::app 在文件末尾追加数据;若要替换现有内容,则使用 std::ios::trunc。如果已知要写入的字符数或字节数,可以使用流的成员函数 write;fwrite 则是对应的 C 风格替代方案。

理解 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 写入的两行内容则保存在 example.txt 中。可以用文本编辑器打开该文件,检查其中的内容。只用于输出的默认 std::ofstream 会在父目录存在时创建文件,并且通常会在打开时清空已有内容。

使用不同模式写入文件

打开模式决定如何处理文件中的现有数据。std::ios::app 会让每次写入都发生在文件末尾,适合需要持续累积的日志或记录。如果文件不存在,只要路径有效且权限允许,以输出模式打开文件就可以创建它。下面的独立示例以追加模式写入一行,不要求 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;
}

提供的运行用例已成功完成,且没有产生控制台输出。代码只会将 Current report 和一个换行符写入 example.txt,替换文件中的任何原有内容。

使用 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 传入 "wb" 而不是 "w",这样就不会发生任何转换。

使用 C 函数 fwrite 写入文件

在 C 风格文件 I/O 中,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 风格 I/O 时,使用 fwrite。检查打开和写入后的状态;如果程序需要确认数据已完整写入,也要检查关闭时是否出错。

常见问题

  1. 如何判断文件是否成功打开? 直接检查流(if (!file)),或在打开文件后立即调用 is_open()。写入前先处理打开失败的情况。

  2. 如何在不覆盖现有内容的情况下追加数据? 使用 std::ios::app 打开输出流。每次写入都会追加到文件末尾。

  3. C++ 会创建不存在的文件吗? 如果父目录存在且进程有权限,输出流以及以 "w" 模式调用的 fopen 都可以创建文件。父目录不存在或权限不足时,打开操作会失败。

  4. 如何写入多行内容? 使用 << 插入换行符,或者将换行符包含在传给 write 的缓冲区中。write 和 fwrite 都不会自动添加换行符。

  5. 应该关闭文件吗? 文件流会在销毁时关闭;显式调用 close() 可以清楚地标示操作已完成。如果需要确认写入完成,应在关闭后检查流状态。对于 C 的 FILE*,调用 fclose 并检查其返回值。