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。检查打开和写入后的状态;如果程序需要确认数据已完整写入,也要检查关闭时是否出错。
常见问题
-
如何判断文件是否成功打开? 直接检查流(
if (!file)),或在打开文件后立即调用is_open()。写入前先处理打开失败的情况。 -
如何在不覆盖现有内容的情况下追加数据? 使用
std::ios::app打开输出流。每次写入都会追加到文件末尾。 -
C++ 会创建不存在的文件吗? 如果父目录存在且进程有权限,输出流以及以
"w"模式调用的fopen都可以创建文件。父目录不存在或权限不足时,打开操作会失败。 -
如何写入多行内容? 使用
<<插入换行符,或者将换行符包含在传给write的缓冲区中。write和fwrite都不会自动添加换行符。 -
应该关闭文件吗? 文件流会在销毁时关闭;显式调用
close()可以清楚地标示操作已完成。如果需要确认写入完成,应在关闭后检查流状态。对于 C 的FILE*,调用fclose并检查其返回值。