我正在使用Visual C++2008。在VC++ 2008中,CFile支持2 ^ 64个大文件。所以我认为CStdioFile也应该支持。

但是,在大于2GB的文件上使用CStdioFile::GetLength()时,我得到了CFileException,下面是代码片段:

void CTestCStdioFileDlg::OnBnClickedButton1()
{
    // TODO: Add your control notification handler code here
    CStdioFile MyFile;
    CString strLine;
    ULONGLONG uLength;

    strLine = _T("This is a line.");

    if (MyFile.Open(_T("C:\\Temp\\MyTest.dat"), CFile::modeCreate | CFile::modeWrite | CFile::shareExclusive | CFile::typeBinary))
    {
        for (UINT uIndex = 0; uIndex = 200000000; uIndex ++)
        {
            MyFile.WriteString(strLine);
            uLength = MyFile.GetLength();
        }

        MyFile.Close();
    }
}

跟踪到CStdio::GetLength()后,我发现以下代码片段将引发异常,如下所示:
   nCurrent = ftell(m_pStream);            -> This will return -1
   if (nCurrent == -1)
      AfxThrowFileException(CFileException::invalidFile, _doserrno,
         m_strFileName);

令人惊讶的是CStdioFile仍然使用ftell而不是_ftelli64来处理流。

然后,我在文档中搜索CStdioFile,在CStdioFile::GetLength上找不到任何文档,唯一相关的是https://docs.microsoft.com/en-us/cpp/mfc/reference/cstdiofile-class?view=vs-2019#seek,它要求我查看fseek文档。但是在fseek文档中,我仍然找不到与文件大小限制有关的任何内容。

最后,我找到了一个第三方站点,该站点指示CStdioFile::GetLength包含错误:http://www.flounder.com/msdn_documentation_errors_and_omissions.htm#CStdioFile::GetLength,但未提供解决方案。

除此之外,几乎没有关于CStdioFile 2GB限制的任何问题或帖子。真是奇怪

我尝试检查iN VC++ 2017的CStdioFile的源代码,它与2008年的源代码相同。

那么,是否有一个无需重写整个CStdioFile类的简单解决方案?

最佳答案

CStdioFile的存在主要是出于以下一个原因:constructor带有FILE*参数。此类的目的是包装C运行时file并通过CFile兼容的接口(interface)公开它。该实现继承了所有C运行时限制,尤其是2GB的文件大小限制。
为了解决这个问题,有几种选择:

  • 如果可能,请完全放弃使用CStdioFile。除非您与(旧式)C代码连接,否则没有显着理由使用它。
  • 如果这不是一个选择,则从CStdioFileoverride派生一个显示文件相对偏移的所有类成员的自定义实现(GetPosition()GetLength()Seek())。所有其他类成员均不受影响,可以简单地继承。 (注意:在实现GetLength()时,请确保恢复当前文件指针。)
    Microsoft提供了对其C运行时的扩展,该扩展提供了64位宽的偏移量(_ftelli64_fseeki64)。

  • 如果需要使用选项2,可以将以下对CStdioFile的扩展用作支持2GB以上文件的直接替代。
    CStdioFileExt.h:
    #pragma once
    
    #include <afx.h>
    
    class CStdioFileExt : public CStdioFile
    {
        DECLARE_DYNAMIC(CStdioFileExt)
    public:
        ULONGLONG GetPosition() const override;
        ULONGLONG GetLength() const override;
        ULONGLONG Seek(LONGLONG lOff, UINT nFrom) override;
    };
    
    CStdioFileExt.cpp:
    #include "CStdioFileExt.h"
    
    ULONGLONG CStdioFileExt::GetPosition() const
    {
        ASSERT_VALID(this);
        ASSERT(m_pStream != NULL);
    
        auto const pos = _ftelli64(m_pStream);
        if (pos == -1L)
            AfxThrowFileException(CFileException::invalidFile, _doserrno, m_strFileName);
        return static_cast<ULONGLONG>(pos);
    }
    
    ULONGLONG CStdioFileExt::GetLength() const
    {
        ASSERT_VALID(this);
    
        auto const nCurrent = _ftelli64(m_pStream);
        if (nCurrent == -1L)
            AfxThrowFileException(CFileException::invalidFile, _doserrno, m_strFileName);
    
        auto nResult = _fseeki64(m_pStream, 0, SEEK_END);
        if (nResult != 0)
            AfxThrowFileException(CFileException::badSeek, _doserrno, m_strFileName);
    
        auto const nLength = _ftelli64(m_pStream);
        if (nLength == -1L)
            AfxThrowFileException(CFileException::invalidFile, _doserrno, m_strFileName);
        nResult = _fseeki64(m_pStream, nCurrent, SEEK_SET);
        if (nResult != 0)
            AfxThrowFileException(CFileException::badSeek, _doserrno, m_strFileName);
    
        return static_cast<ULONGLONG>(nLength);
    }
    
    ULONGLONG CStdioFileExt::Seek(LONGLONG lOff, UINT nFrom)
    {
        ASSERT_VALID(this);
        ASSERT(nFrom == begin || nFrom == end || nFrom == current);
        ASSERT(m_pStream != NULL);
    
        if (_fseeki64(m_pStream, lOff, nFrom) != 0)
            AfxThrowFileException(CFileException::badSeek, _doserrno, m_strFileName);
    
        auto const pos = _ftelli64(m_pStream);
        return static_cast<ULONGLONG>(pos);
    }
    
    IMPLEMENT_DYNAMIC(CStdioFileExt, CStdioFile)
    

    关于c++ - CStdioFile无法使用大于2GB的文件?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/62097271/

    10-09 13:37