【C++与Linux基础】文件篇 – 语言特性上的文件操作
在 C++ 中进行文件操作,主要依赖两种方式:
- C++ 标准库(
<fstream>)—— 现代 C++ 推荐方式,跨平台,面向对象风格 - C 风格文件操作(
<cstdio>中的fopen、fread等)—— 仍然大量存在,尤其在与 C 代码混合、性能敏感或 Linux 系统编程场景中
此外,在 Linux 环境下,C++ 程序也经常直接使用系统调用(<unistd.h>、open、read、write 等)来实现更底层的文件/IO 操作。
下面按使用频率和推荐度逐一对比说明。
1. C++ 标准库方式(推荐现代写法)
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
int main()
{
// ------------------------------
// 1. 写文件(推荐方式)
// ------------------------------
std::ofstream ofs("output.txt", std::ios::out | std::ios::trunc);
if (!ofs.is_open()) {
std::cerr << "无法打开文件\n";
return 1;
}
ofs << "Hello, C++ file!\n";
ofs << "当前行: " << __LINE__ << "\n";
ofs << 3.14159 << " " << 42 << "\n";
// 也可以用格式化(C++20 之后更方便)
// ofs << std::format("pi = {:.4f}\n", 3.1415926535);
ofs.close(); // 通常可以省略,析构时自动关闭
// ------------------------------
// 2. 读文件 - 按行读取(最常用)
// ------------------------------
std::ifstream ifs("output.txt");
if (!ifs.is_open()) {
std::cerr << "无法打开文件\n";
return 1;
}
std::string line;
while (std::getline(ifs, line)) {
std::cout << line << '\n';
}
// ------------------------------
// 3. 一次性读入全部内容(适合小文件)
// ------------------------------
std::ifstream in("config.ini", std::ios::in | std::ios::binary);
if (in) {
std::string content((std::istreambuf_iterator<char>(in)),
std::istreambuf_iterator<char>());
std::cout << "文件全部内容长度: " << content.size() << '\n';
}
// ------------------------------
// 4. 读写二进制文件
// ------------------------------
struct Record {
int id;
double value;
char name[32];
};
// 写
std::ofstream bin_out("data.bin", std::ios::binary);
Record r{1001, 3.14159, "SensorA"};
bin_out.write(reinterpret_cast<const char*>(&r), sizeof(r));
// 读
std::ifstream bin_in("data.bin", std::ios::binary);
Record r2;
if (bin_in.read(reinterpret_cast<char*>(&r2), sizeof(r2))) {
std::cout << r2.id << " " << r2.value << " " << r2.name << '\n';
}
return 0;
}
C++ 文件流常用状态与标志
| 打开模式 | 说明 | 常用组合 |
|---|---|---|
std::ios::in | 以读方式打开 | 读文件 |
std::ios::out | 以写方式打开(默认清空) | 写文件 |
std::ios::app | 追加模式 | 日志文件 |
std::ios::trunc | 如果文件存在则清空(out 默认) | 重写文件 |
std::ios::binary | 二进制模式(不转换换行符) | 读写图片、struct、protobuf 等 |
std::ios::ate | 打开后立即定位到文件末尾 | 追加写 |
2. C 风格文件操作(仍然非常常见)
#include <cstdio>
#include <cstring>
int main()
{
// 写文件
FILE* fp = fopen("log.txt", "w");
if (!fp) {
perror("fopen");
return 1;
}
fprintf(fp, "Time: %ld Error: %s\n", time(nullptr), "disk full");
fputs("critical alert\n", fp);
fclose(fp);
// 读文件(按块读)
char buf[4096];
FILE* in = fopen("large.bin", "rb");
if (in) {
size_t n;
while ((n = fread(buf, 1, sizeof(buf), in)) > 0) {
// 处理 buf[0..n-1]
}
fclose(in);
}
// 定位 + 追加写
FILE* log = fopen("server.log", "a+");
fseek(log, 0, SEEK_END); // 可省略,因为 a 模式默认追加
fprintf(log, "[INFO] connection from %s\n", "192.168.1.100");
return 0;
}
3. Linux 系统调用方式(底层、最灵活)
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <cstring>
#include <iostream>
int main()
{
// 打开文件
int fd = open("raw.dat", O_RDWR | O_CREAT, 0644);
if (fd == -1) {
perror("open");
return 1;
}
// 写
const char* msg = "Hello from syscall\n";
write(fd, msg, strlen(msg));
// 定位
off_t pos = lseek(fd, 0, SEEK_SET);
if (pos == -1) perror("lseek");
// 读
char buf[128];
ssize_t n = read(fd, buf, sizeof(buf)-1);
if (n > 0) {
buf[n] = '\0';
std::cout << "Read: " << buf;
}
close(fd);
// 追加写示例
int logfd = open("/var/log/app.log", O_WRONLY | O_APPEND | O_CREAT, 0644);
if (logfd != -1) {
const char* line = "ERROR: connection timeout\n";
write(logfd, line, strlen(line));
close(logfd);
}
return 0;
}
4. 三种方式对比总结(2025–2026 视角)
| 方式 | 跨平台 | 易用性 | 性能 | 推荐场景 | 现代 C++ 推荐度 |
|---|---|---|---|---|---|
std::fstream | 是 | ★★★★★ | 中等 | 日常开发、配置、日志、中等大小文件 | ★★★★★ |
C 风格 FILE* | 是 | ★★★★ | 高 | 与 C 库交互、性能敏感、嵌入式、老代码 | ★★★ |
| Linux 系统调用 | 否 | ★★ | 最高 | 高性能服务器、驱动开发、大文件处理、零拷贝 | ★★(特定场景) |
5. 现代 C++ 写文件操作的推荐实践(2024+)
- 优先使用
std::ofstream/std::ifstream - 小文件直接读入
std::string(如上文方式) - 大文件建议分块读取(避免一次性读入全部内容)
- 总是检查打开是否成功
- RAII 原则:尽量依赖析构自动关闭(不显式
close()) - 日志类场景 → 考虑
std::ofstream+std::ios::app - 二进制数据 → 务必加上
std::ios::binary - 异常安全 → 可开启
ifs.exceptions(std::ios::failbit | std::ios::badbit);
6. 常见问题快速定位
- 文件打不开 → 检查路径、权限、是否被其他进程占用
- Windows 和 Linux 换行符不同 → 文本模式下自动处理,二进制模式需注意
- 写不进去 → 忘记
flush()或close(),或磁盘满 - 读到乱码 → 忘记
std::ios::binary或编码问题
下一节可以深入讲:
- RAII 封装的文件类
- mmap 内存映射文件
- 异步文件 IO(io_uring)
- 大文件分块读写 + 进度显示
你更想往哪个方向继续?或者有具体场景(日志、配置、二进制、超大文件等)想看最优实现?