序
对于c/c++来说,写文件是必不可少的事情。但是如果大家不仔细研究,真的会掉进某些坑里。
在某些紧急情况下,可能要保证数据安全且及时的写到磁盘上,否则就会有丢失的风险。
小文件可能还不那么明显,对于大文件来说,按照平常的方式,可能就出事了。
今天我就带大家来聊一聊这个问题。

读写文件
c语言中文件操作用的是 FILE*, 以及与之相关的打开,读,写,关闭等函数。
对应的是 fopen、fread、fwrite、fclose 等(fgets fputs 等就不在此继续展开了)。
但是在写文件的时候,我们如何保证数据被正确及时的写到磁盘上面了呢?
可能大家会说调用 fflush;很显然如果真的是这样,那今天我也不会写这篇文章了。
那为什么 fflush 不对呢?
缓存
我们可能都知道,文件读写,或者说一般的IO操作都会有缓存。
即磁盘文件被操作系统映射到系统的某个内存中缓存了;我们平常读写是会和缓存打交道。
操作系统根据我们当前操作情况,在缓存与磁盘文件之间进行数据交互。
而对于 fflush 来说,只能保证数据已经被写到了操作系统的文件缓存,不能保证已经写到了磁盘!
只有在 fclose 的时候才能确保缓存数据全部写到磁盘,然后关闭文件。
大家可能会问,那就在 fwrite 写完数据后,直接 fclose 不就行了。
是的,对于小文件来说可能确实如此。
但是对于大文件的时候,可能之前缓存里的数据还没有及时写到磁盘,后面的数据也需要写入;
虽然在 fclose 后会强制将缓存数据写到磁盘,但是可能会需要一点时间,而不巧此时断电了。
解决方案
windows提供的API中,有 CreateFile、ReadFile、WriteFile 和 CloseHandle 来处理IO操作。
其中 CreateFile 有个 dwFlagsAndAttributes 的参数,可以传递不同的属性。
对于我们遇到的问题可以向该参数设置为 FILE_FLAG_NO_BUFFERING 或 FILE_FLAG_WRITE_THROUGH 或者设置成它们相或后的值。
FILE_FLAG_NO_BUFFERING 表示读写文件时,不创建缓存;
FILE_FLAG_WRITE_THROUGH 表示在数据写到缓存后,立刻写入磁盘。
即要写入的文件如下打开即可:
// 打开文件
HANDLE hFileDst = ::CreateFileA(dst.toLocal8Bit().data(),
GENERIC_WRITE,
0,
NULL,
OPEN_ALWAYS,
/*FILE_FLAG_NO_BUFFERING |*/ FILE_FLAG_WRITE_THROUGH, // 设置flag
NULL);
if (hFileDst == INVALID_HANDLE_VALUE)
{
CloseHandle(hFileSrc);
return;
}
// 数据写入
int ret = ::WriteFile(hFileDst, m_dat, dwReadSize, &dwWriteSize, NULL);
需要注意的是 FILE_FLAG_NO_BUFFERING 设置后需要字节对应,文件存取的字节数必须是扇区尺寸的整倍数。
例如,如果扇区尺寸是512字节,程序就可以读或者写512,1024或者2048字节,但不能够是335,981或者7171字节。(这是MSDN上的翻译)
总结
缓存的存在是操作系统为了更好方便我们读写数据,避免因为磁盘的慢速,影响读写操作;
但是如果不清楚其中的原理,可能在特殊情况时,不知道如何处理问题。
当然如果大家没有紧急情况出现,或者对数据写入时间不敏感,可以直接忽略;还是用标准写文件的方式,毕竟更加通用。
相关文章:
民国时期:辛亥革命、北伐战争、抗日战争 09-14
宋朝二程是谁?“二程”才高,却伤害了无数女性 09-14
普洱茶用密封袋存放,还会持续转化吗?普洱茶的保存收藏注意事项 09-13
北宋有夜市吗?为何从宋代就有夜市了?宋代的夜如此美妙 09-13
民国初年政党派发展演变简史 09-12
南宋被谁灭亡?金国把南宋打得苟延残喘,为何还是无法彻底灭掉南宋? 09-12
同样是公主,为何宋朝的公主没有唐朝公主的名气和权力? 09-11
金国打宋朝是怎么回事?金军灭北宋始末,一起来看看吧 09-11
民国北洋军阀 民国北洋时代的十大军阀,谁能排第一? 09-10
宋朝以后,河南就没了话语权,更遑论名牌大学 09-10