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

CFile & CStdioFile produces different results in writing & reading

根据我的理解,当使用 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会写出比预期少的数据量。

也有很多不同的读取数据报告。

为什么?

当我 运行 您的代码处于调试模式时,我得到以下 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 而没有调用 [=14] =]/flush.

作为快速修复,您可以在 CheckRead 函数的底部添加以下行:

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