| 如何安全的保存数据到文件 void SaveSetting (const char* strFile, const char* pBuffer, int nLength)
{
FILE * pFL = fopen ("c://setting.txt", "w+") ;
fwrite (pBuffer, 1, nLength, pFL) ;
fclose (pFL) ;
}
有问题吗?绝大部分人会认为没错,对,确实没错@_@。。。先别仍鸡蛋。让我们考虑这样一种情况:在写文件的时候遇到断电或死机等异常退出,那目标文件就会不完整,要是配置信息这样的重要文件,损坏后可能就导致程序运行不了。也许有人会说这是杞人忧天,写文件也就几十毫秒,被中断的概率不亚于中彩票。。。最开始我也没怀疑到,但很高兴上帝如此偏爱我,总是让我偶尔(尤其关机时)又不可重现的碰到这种情况。而且,随着配置信息的增多,中奖概率也在上升。很多时候,越是这种看不上眼的小东西就越容易被人忽视。 /// 定义
class FCSafeSaveFile
{
public:
FCSafeSaveFile (LPCTSTR szFilename, bool bSameExt=false) ;
~FCSafeSaveFile() ;
bstr_t GetTempFilename() const ;
void ReplaceFile() ;
void DisableReplace() ;
void EnableReplace() ;
private:
bstr_t m_strDest ;
bstr_t m_strTemp ;
BOOL m_bHasReplace ;
};
/// 构造函数,在同一目录生成一个临时文件。
FCSafeSaveFile::FCSafeSaveFile (LPCTSTR szFilename, bool bSameExt=false)
{
m_strDest = szFilename ;
m_bHasReplace = FALSE ;
// get temp path
TCHAR szPath[MAX_PATH],
szDriver[_MAX_DRIVE],
szDir[MAX_PATH] ;
::_tsplitpath (szFilename, szDriver, szDir, NULL, NULL) ;
::_tmakepath (szPath, szDriver, szDir, NULL, NULL) ;
// get a temp filename
TCHAR szTmp[MAX_PATH] ;
::GetTempFileName (szPath, _T("foo"), 0, szTmp) ;
m_strTemp = szTmp ;
if (bSameExt)
{
TCHAR szExt[_MAX_EXT] ;
::_tsplitpath (szFilename, NULL, NULL, NULL, szExt) ;
bstr_t strNew = m_strTemp + szExt ;
::_trename (m_strTemp, strNew) ;
m_strTemp = strNew ;
}
}
/// 安全替换文件,请确认所有作用于临时文件的句柄都已关闭
void FCSafeSaveFile::ReplaceFile()
{
// delete exist file
if (!PathFileExists(m_strDest))
{
// rename
::_trename (m_strTemp, m_strDest) ;
}
else
{
::SetFileAttributes (m_strDest, FILE_ATTRIBUTE_NORMAL) ;
if (::DeleteFile (m_strDest))
{
// rename
::_trename (m_strTemp, m_strDest) ;
}
else
{
assert(false) ;
::DeleteFile (m_strTemp) ;
}
}
m_bHasReplace = TRUE ;
}
/// 没有人希望被要求必须调用 ReplaceFile,我们交给编译器和析构来做这事
FCSafeSaveFile::~FCSafeSaveFile()
{
ReplaceFile() ;
if (PathFileExists(m_strTemp))
{
assert(false) ;
::DeleteFile (m_strTemp) ;
assert (!PathFileExists(m_strTemp)) ;
}
}
现在我们可以用我们的类修改上面的代码,让它更安全: void SaveSetting (const char* strFile, const char* pBuffer, int nLength)
{
FCSafeSaveFile ssf (TEXT("c://setting.txt")) ;
FILE * pFL = fopen (ssf.GetTempFilename(), "w+") ;
fwrite (pBuffer, 1, nLength, pFL) ;
fclose (pFL) ;
}
这样,我们就保证了目标文件的安全似乎很完美了,考虑一个问题,如果你的代码维护者不清楚这段代码作用,在其中加入了一些return语句中途返回,文件替换操作还是会被执行,还是不完整。 void SaveSetting (const char* strFile, const char* pBuffer, int nLength)
{
FCSafeSaveFile ssf (TEXT("c://setting.txt")) ;
ssf.DisableReplace() ;
if (nLength != 10)
return ;
FILE * pFL = fopen (ssf.GetTempFilename(), "w+") ;
fwrite (pBuffer, 1, nLength, pFL) ;
fclose (pFL) ;
ssf.EnableReplace() ;
}
这样我们就从结构上尽量避免了维护者可能带来的问题。 这里,我们封装了一个非常非常简单的类,但它很有用,作为构建高层软件的基础,它能帮您省却今后的很多麻烦。
|
如何安全的保存数据到文件
最新推荐文章于 2024-12-23 08:45:41 发布
本文介绍了一种通过创建临时文件并在完成后重命名的方式确保文件安全保存的技术。这种方法避免了因异常中断而导致的数据不完整问题。
2084

被折叠的 条评论
为什么被折叠?



