file - CFile 和 CStdioFile 在写入和读取时产生不同的结果

标签 file visual-c++ mfc cfile

据我了解,使用 typeBinary 标志创建时,CFile 和 CStdioFile 的工作方式应该相同,只是后者正在缓冲数据,因此具有更好的性能。

所以我编写了以下代码来确认这一点:

ULONGLONG GetRand(ULONGLONG uMax)
{
    UINT uValue;

    if (rand_s(&uValue) == 0)
        return  (ULONGLONG)(((double)uValue / (double)UINT_MAX) * uMax);
    else
        return  0;
}

void CheckOffset(CFile& File1, CFile& File2)
{
    ULONGLONG uOffset1, uOffset2;
    CString strMsg;

    uOffset1 = File1.GetPosition();
    uOffset2 = File2.GetPosition();

    if (uOffset1 != uOffset2)
    {
        strMsg.Format(_T("Difference offset. Offset1 = %I64u. Offset2 = %I64u."), uOffset1, uOffset2);
        AfxMessageBox(strMsg);
    }
}

void CheckLength(CFile& File1, CFile& File2)
{
    ULONGLONG uLength1, uLength2;
    CString strMsg;

    uLength1 = File1.GetLength();
    uLength2 = File2.GetLength();

    if (uLength1 != uLength2)
    {
        strMsg.Format(_T("Difference length. Length1 = %I64u. Length2 = %I64u."), uLength1, uLength2);
        AfxMessageBox(strMsg);
    }
}

void CheckSeek(CFile& File1, CFile& File2, ULONGLONG uOffset)
{
    ULONGLONG uOffset1, uOffset2;
    CString strMsg;

    uOffset1 = File1.Seek(uOffset, CFile::begin);
    uOffset2 = File2.Seek(uOffset, CFile::begin);

    if (uOffset1 != uOffset2)
    {
        strMsg.Format(_T("Difference seek results. Offset1 = %I64u. Offset2 = %I64u."), uOffset1, uOffset2);
        AfxMessageBox(strMsg);
    }
}

void CheckRead(CFile& File1, CFile& File2, UINT uSize)
{
    BYTE lpBuf1[4096], lpBuf2[4096];
    UINT uRead1, uRead2;
    CString strMsg;

    //  Read buffer from file1 & file2
    uRead1 = File1.Read(lpBuf1, uSize);
    uRead2 = File2.Read(lpBuf2, uSize);

    if ((uRead1 != uRead2) || (memcmp(lpBuf1, lpBuf2, uRead1) != 0))
    {
        strMsg.Format(_T("Difference read results. uRead1 = %u. uRead2 = %u."), uRead1, uRead2);
        AfxMessageBox(strMsg);
    }
}

void CTestStdioFile64Dlg::OnBnClickedButton1()
{
    // TODO: Add your control notification handler code here
    CFile File1;
    CStdioFile File2;
    UINT uSize;
    BYTE lpBuf[4096];
    CString strMsg;

    if (File1.Open(_T("F:\\Temp\\Test1.dat"), CFile::modeCreate | CFile::modeReadWrite | CFile::shareExclusive | CFile::typeBinary))
    {
        if (File2.Open(_T("F:\\Temp\\Test2.dat"), CFile::modeCreate | CFile::modeReadWrite | CFile::shareExclusive | CFile::typeBinary))
        {
            CheckOffset(File1, File2);
            CheckLength(File1, File2);

            //  Write data
            for (UINT uIndex = 0; uIndex < 20000; uIndex ++)
            {
                //  Generate a randome size for write
                uSize = (UINT)GetRand(4096);

                //  Generate buffer with random data
                for (UINT j = 0; j < uSize; j++)
                    lpBuf[j] = (BYTE)GetRand(255);

                //  Write buffer to file1 & file2
                File1.Write(lpBuf, uSize);
                File2.Write(lpBuf, uSize);

                File1.Flush();
                File2.Flush();

                CheckOffset(File1, File2);
                CheckLength(File1, File2);

                //  Seek to a randome location
                CheckSeek(File1, File2, GetRand(File1.GetLength()));

                //  Generate a randome size for read
                uSize = (UINT)GetRand(4096);

                CheckRead(File1, File2, uSize);
                CheckOffset(File1, File2);
            }

            File2.Close();
        }

        File1.Close();
    }
}

令我惊讶的是,在测试过程中,出现了很多 CFileException 异常,因为 CStdioFile::Write 写出的数据量比预期要少。

此外,还报告了许多不同的读取数据。

为什么?

最佳答案

当我在 Debug模式下运行您的代码时,我收到以下 ASSERT。

"Flush between consecutive read and write.", !stream.has_any_of(_IOREAD)

原因如下:

来自 C Standard documentation : (第 306 页,7.21.5.3)。

When a file is opened with update mode ('+' as the second or third character in the above list of mode argument values), both input and output may be performed on the associated stream. However, output shall not be directly followed by input without an intervening call to the fflush function or to a file positioning function (fseek, fsetpos, or rewind), and input shall not be directly followed by output without an intervening call to a file positioning function, unless the input operation encounters endof-file.

在您的代码中,您在循环结束时调用 CheckRead 函数,但调用 File1.WriteFile2.Write > 在下一次迭代中,无需调用 fseek/flush

作为快速解决方案,您可以在 CheckRead 函数的底部添加以下行:

File1.Seek(0, SEEK_CUR);
File2.Seek(0, SEEK_CUR);

关于file - CFile 和 CStdioFile 在写入和读取时产生不同的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62128545/

相关文章:

python - 需要在 Python 中使用多个文件进行编程方面的帮助

file - .empty 文件有什么用?

c++ - MFC 文档/ View 体系结构 - 创建没有初始 View 的文档

c++ - CArray 和 CMap 的运算速度

javascript - 加载脚本: innerHTML vs.appendChild

c++ - 使用多线程将标准输出重定向到文件

c++ - GCC 函数的 Visual C++ 版本

c++ - 在 C++ 中顺序对齐函数

c++ - 原始数组和 std::array 在 clang++ 和 VC++ 上的不同迭代器行为

c++ - 在 CArray 中赋值的简单方法