【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>intmain(){// ------------------------------// 1. 写文件(推荐方式)// ------------------------------std::ofstreamofs("output.txt",std::ios::out|std::ios::trunc);if(!ofs.is_open()){std::cerr<<"无法打开文件\n";return1;}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::ifstreamifs("output.txt");if(!ifs.is_open()){std::cerr<<"无法打开文件\n";return1;}std::string line;while(std::getline(ifs,line)){std::cout<<line<<'\n';}// ------------------------------// 3. 一次性读入全部内容(适合小文件)// ------------------------------std::ifstreamin("config.ini",std::ios::in|std::ios::binary);if(in){std::stringcontent((std::istreambuf_iterator<char>(in)),std::istreambuf_iterator<char>());std::cout<<"文件全部内容长度: "<<content.size()<<'\n';}// ------------------------------// 4. 读写二进制文件// ------------------------------structRecord{intid;doublevalue;charname[32];};// 写std::ofstreambin_out("data.bin",std::ios::binary);Record r{1001,3.14159,"SensorA"};bin_out.write(reinterpret_cast<constchar*>(&r),sizeof(r));// 读std::ifstreambin_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';}return0;}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>intmain(){// 写文件FILE*fp=fopen("log.txt","w");if(!fp){perror("fopen");return1;}fprintf(fp,"Time: %ld Error: %s\n",time(nullptr),"disk full");fputs("critical alert\n",fp);fclose(fp);// 读文件(按块读)charbuf[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");return0;}3. Linux 系统调用方式(底层、最灵活)
#include<unistd.h>#include<fcntl.h>#include<sys/stat.h>#include<sys/types.h>#include<cstring>#include<iostream>intmain(){// 打开文件intfd=open("raw.dat",O_RDWR|O_CREAT,0644);if(fd==-1){perror("open");return1;}// 写constchar*msg="Hello from syscall\n";write(fd,msg,strlen(msg));// 定位off_t pos=lseek(fd,0,SEEK_SET);if(pos==-1)perror("lseek");// 读charbuf[128];ssize_t n=read(fd,buf,sizeof(buf)-1);if(n>0){buf[n]='\0';std::cout<<"Read: "<<buf;}close(fd);// 追加写示例intlogfd=open("/var/log/app.log",O_WRONLY|O_APPEND|O_CREAT,0644);if(logfd!=-1){constchar*line="ERROR: connection timeout\n";write(logfd,line,strlen(line));close(logfd);}return0;}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)
- 大文件分块读写 + 进度显示
你更想往哪个方向继续?或者有具体场景(日志、配置、二进制、超大文件等)想看最优实现?