seekg无法正确处理大小为4294967295字节的文件。

10

我发现在VS2010中,当打开大小恰好为4294967295字节的文件时,seekg函数无法正常工作。

我正在使用简单的代码:

#include <iostream>
#include <fstream>
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
    std::ifstream file;

    // cmd: fsutil file createnew tmp.txt 4294967295
    file.open(L"c:/tmp.txt", ifstream::in | ifstream::binary);

    if(!file.is_open())
        return -1;

    file.seekg(0, std::ios::end);

    auto state = file.rdstate();

    // this condition shoots only when size of the file is equal to 4294967295
    if((state & ifstream::failbit)==ifstream::failbit)
    {
        std::cout << "seekg failed";
    }

    // after seekg failed, tellg returns 0
    std::streampos endPos = file.tellg();

    return 0;
}

文件编号为4294967294和4294967296的代码可以正常运行,没有问题。

有人知道解决这个问题的方法吗?

更新:

看起来问题出在这里:

template<class _Statetype>
class fpos
{
 __CLR_OR_THIS_CALL operator streamoff() const
 { // return offset
 return ((streamoff)(_Myoff + _FPOSOFF(_Fpos)));
 }
}

恰好在

_FPOSOFF(_Fpos)

在哪里

#define _FPOSOFF(fp) ((long)(fp))

因此,它将4294967295转换为-1!

换句话说,这样的代码将失败。

//returns -1, even if sizeof(fpos_t)=8
fpos_t pos = _FPOSOFF(4294967295);

_Myoff,_Fpos和streamoffset都是64位的。

如果所有类型都是64位,为什么要进行这种转换?我也不知道))


1
注意:4294967295 是 0xffffffff。可能有某些32位代码出现了问题。 - ecatmur
1
考虑到4294967295与-1具有相同的位模式,如果这是运行时错误,我并不感到惊讶。但很遗憾,我没有解决方法可以提供... - NPE
1
这是一个错误,在Visual Studio 2012中已经修复:_FPOSOFF现在转换为long long,从而避免了截断。 - James McNellis
3个回答

7

在内部,流实现有一个名为“_BADOFF”的常量,其值为0xffffffff,当寻求失败时返回该值。在这种情况下,查找成功了,但从查找中返回的值等于失败代码,导致流包装器错误地设置其故障代码。

_BADOFF被定义为一个64位类型,只是被赋予了一个愚蠢的值。

作为解决方法,您可以向前查找1个字节,然后读取一个字节。

file.seekg(-1, std::ios::end);
char temp; file >> temp;

然而请注意,每当特定文件偏移被搜索时,此错误将显现出来,因此如果您在其中随机搜索位置,则对于较大的文件仍可能存在问题。例如,如果您的文件大一个字节,这个-1搜索将失败,因此这不是一般解决方案。
OP已扩展了他们的问题,所以我也会扩展我的回答。是的,在比较之前,寻址值使用了不安全的转换进行了强制转换。这似乎不影响超过文件中该点的查找能力,因为它仅用于与错误值进行比较 - 流仍具有正确的偏移量。但是,它似乎是_BADOFF首先存在疑问的根本原因,因为源代码中_BADOFF设置为“-1”,并且将遭受相同的转换,截断为0xffffffff。
因此,库的修复可能是修复转换(假设没有其他副作用),但是为了解决问题,您只需要避免寻址底部32位已设置的位置。从我所看到的情况来看,它可以正常地超越那个位置。

4
WTF是“What a Terrific Failure”的缩写,意思是“非常糟糕的失败”。 - Michał Herman
了不起?你是说“可怕”吗? - R. Martinho Fernandes
3
他的意思是Triumphant,不要叫他Shirley。 - Lightness Races in Orbit
我已经更新了我的问题,问题在于转换为长整型。 - Rusty
@user1897425:请不要事后修改问题。这是您发布的问题的答案 - 如果您有新问题,请发布新问题。您的编辑应该被撤销,并将此答案标记为“已接受”。 - Lightness Races in Orbit
显示剩余2条评论

5
这确实是Visual C ++ 2010中的一个错误。两年前在Microsoft Connect上报告了这个问题:"std :: fstream 即使在x64平台上也使用32位int作为pos_type"(bug标题不正确;实际上是由_FPOSOFF中的此错误引起的症状)。
这个错误已经在Visual C ++ 2012中得到修复,其中_FPOSOFF 的定义为:
#define _FPOSOFF(fp)  ((long long)(fp))

如果您有能力这样做,我们建议您升级到Visual C++ 2012。


谢谢,我也打算在VS2012上检查一下,但是转到另一个IDE并不总是容易的。 - Rusty

2

此时有两个不太好的解决方案

  1. 在stdio.h中进行修复

    #define _FPOSOFF(fp) ((long long)(fp))

  2. 升级到VS2012(这也很遗憾)

    这是一个错误,已在Visual Studio 2012中得到修复:_FPOSOFF现在转换为long long,从而避免截断。- James McNellis


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接