2

据我了解,当使用 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();
    }
}

令我惊讶的是,在测试过程中,由于 CStdioFile::Write 写入的数据量比预期的少,因此引发了许多 CFileException。

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

为什么?

4

1 回答 1

2

当我在调试模式下运行您的代码时,我得到以下断言。

"在连续读写之间刷新。", !stream.has_any_of(_IOREAD)

原因如下:

来自 C标准文档:(第 306 页,7.21.5.3)。

当以更新模式打开文件时(“+”作为上述模式参数值列表中的第二个或第三个字符),可以在关联的流上执行输入和输出。但是,如果没有对 fflush 函数或文件定位函数(fseek、fsetpos 或 rewind)的干预调用,则输出不应直接跟随输入,并且在没有对文件定位的干预调用的情况下,输入不应直接跟随输出函数,除非输入操作遇到文件结束。

在您的代码中,您CheckRead在循环结束时调用 for 函数,但在下一次迭代中调用 and 而不File1.Write调用/ 。File2.Writefseekflush

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

File1.Seek(0, SEEK_CUR);
File2.Seek(0, SEEK_CUR);
于 2020-06-01T09:40:11.270 回答