在C++开发场景中,将文件写入移动硬盘时,用户突然拔出设备会直接导致写入操作中断,触发底层IO错误。这类错误不属于常规的逻辑错误,往往会导致程序抛出未捕获的异常进而崩溃,需要针对性设计捕获和处理逻辑。

C++文件写入的常见IO异常类型
移动硬盘拔出触发的异常属于底层输入输出错误,不同C++标准库组件的表现不同:使用C标准库的stdio函数时,错误会反映在返回值中;使用C++标准库的fstream时,会设置流的状态位,若开启异常开关还会抛出std::ios_base::failure类型的异常。
fstream流状态位说明
- badbit:表示发生致命的底层错误,比如移动硬盘拔出导致的硬件IO错误,流将无法再继续使用
- failbit:表示操作失败,比如写入的数据格式不匹配,或者写入过程中设备断开
- eofbit:表示到达文件末尾,和硬件拔出无关
使用fstream捕获移动硬盘拔出异常
首先需要开启fstream的异常抛出开关,默认情况下fstream不会抛出异常,只会设置状态位,开启后当对应状态位被设置时就会抛出std::ios_base::failure异常。
#include <fstream>
#include <iostream>
#include <exception>
int main() {
// 打开移动硬盘上的文件,开启badbit和failbit的异常抛出
std::ofstream out_file;
out_file.exceptions(std::ofstream::badbit | std::ofstream::failbit);
try {
// 假设移动硬盘挂载路径为/media/usb/test.txt
out_file.open("/media/usb/test.txt");
if (!out_file.is_open()) {
std::cout << "文件打开失败" << std::endl;
return -1;
}
// 循环写入数据,模拟长时间写入场景
for (int i = 0; i < 1000; ++i) {
out_file << "这是第" << i << "行测试数据" << std::endl;
// 每次写入后刷新缓冲区,模拟真实写入场景
out_file.flush();
}
out_file.close();
std::cout << "文件写入完成" << std::endl;
} catch (const std::ios_base::failure& e) {
// 捕获到IO异常,说明发生了比如移动硬盘拔出之类的错误
std::cout << "捕获到IO异常:" << e.what() << std::endl;
if (out_file.is_open()) {
out_file.close();
}
return -1;
} catch (const std::exception& e) {
// 捕获其他标准异常
std::cout << "捕获到其他异常:" << e.what() << std::endl;
return -1;
}
return 0;
}
使用C标准库stdio捕获异常
如果使用C的stdio函数进行文件操作,没有异常机制,需要通过检查函数返回值和errno来判断是否发生错误,移动硬盘拔出后,fwrite等写入函数会返回小于预期写入数量的数值,同时errno会被设置为EIO。
#include <cstdio>
#include <cstring>
#include <cerrno>
#include <iostream>
int main() {
// 打开移动硬盘上的文件
FILE* fp = fopen("/media/usb/test.txt", "w");
if (fp == nullptr) {
std::cout << "文件打开失败,错误原因:" << strerror(errno) << std::endl;
return -1;
}
char write_data[1024];
memset(write_data, 'a', sizeof(write_data));
// 循环写入数据
for (int i = 0; i < 100; ++i) {
// 写入1024字节数据
size_t write_size = fwrite(write_data, 1, sizeof(write_data), fp);
if (write_size != sizeof(write_data)) {
// 写入数量不符合预期,说明发生错误
std::cout << "写入失败,错误原因:" << strerror(errno) << std::endl;
fclose(fp);
return -1;
}
fflush(fp);
}
fclose(fp);
std::cout << "文件写入完成" << std::endl;
return 0;
}
避坑注意事项
在处理这类异常时,有几个常见的坑需要避免:
- 不要忽略流的flush操作,移动硬盘写入有缓存,不主动刷新缓冲区的话,即使设备拔出,也可能因为数据还在内存缓存中没有显示错误,导致错误发现滞后
- 捕获异常后一定要关闭打开的文件流,避免资源泄漏,即使流已经处于错误状态,关闭操作也是安全的
- 不要假设写入成功就代表数据已经完整存储到移动硬盘,底层可能还有缓存,必要时可以做写入后的校验逻辑
- 如果是跨平台程序,Windows和Linux下的错误信息描述不同,不要硬编码错误信息判断,尽量通过状态位或者errno数值判断
扩展:RAII方式封装文件操作
为了避免忘记关闭文件,可以使用RAII方式封装文件操作,在析构函数中自动关闭文件,同时结合异常捕获逻辑,让代码更健壮。
#include <fstream>
#include <iostream>
#include <exception>
class SafeFileWriter {
private:
std::ofstream out_file;
public:
SafeFileWriter(const char* file_path) {
out_file.exceptions(std::ofstream::badbit | std::ofstream::failbit);
out_file.open(file_path);
}
~SafeFileWriter() {
if (out_file.is_open()) {
try {
out_file.close();
} catch (...) {
// 析构函数中不要抛出异常,吞掉关闭时的异常
}
}
}
void write_line(const std::string& line) {
out_file << line << std::endl;
out_file.flush();
}
};
int main() {
try {
SafeFileWriter writer("/media/usb/test.txt");
for (int i = 0; i < 1000; ++i) {
writer.write_line("这是第" + std::to_string(i) + "行测试数据");
}
std::cout << "文件写入完成" << std::endl;
} catch (const std::ios_base::failure& e) {
std::cout << "捕获到IO异常:" << e.what() << std::endl;
return -1;
}
return 0;
}
C++_IO异常捕获文件写入异常处理移动硬盘拔出异常文件流错误处理修改时间:2026-06-15 12:51:24