【问题标题】:seekg() failing mysteriouslyseekg() 神秘地失败
【发布时间】:2010-03-07 02:58:36
【问题描述】:

我有一个 2884765579 字节的文件。使用此函数进行双重检查,返回该数字:

size_t GetSize() {
       const size_t current_position = mFile.tellg();
       mFile.seekg(0, std::ios::end);
       const size_t ret = mFile.tellg();
       mFile.seekg(current_position);
       return ret;
    }

然后我会这样做:

mFile.seekg(pos, std::ios::beg);
// pos = 2883426827, which is < than the file size, 2884765579

这会设置故障位。 errno 没有改变。我可以采取哪些步骤来解决此问题?


绝对肯定

  • 文件大小真的是2884765579
  • pos 真的是 2884765579
  • 在.seekg()之前没有设置failbit
  • failbit 是在 .seekg() 之后设置的,中间没有其他调用
  • 文件以二进制标志打开

编辑:以防万一有人遇到同样的问题.. 使用我编写的这段代码(仅适用于 Windows),减少您的麻烦:

class BinaryIFile
{
public:
    BinaryIFile(const string& path) : mPath(path), mFileSize(0) {
        mFile = open(path.c_str(), O_RDONLY | O_BINARY);

        if (mFile == -1)
            FATAL(format("Cannot open %s: %s") % path.c_str() % strerror(errno));
    }
    ~BinaryIFile() {
        if (mFile != -1)
            close(mFile);
    }

    string GetPath() const { return mPath; }
    int64 GetSize() {
        if (mFileSize)
            return mFileSize;

        const int64 current_position = _telli64(mFile);
        _lseeki64(mFile, 0, SEEK_END);
        mFileSize = _telli64(mFile);
        _lseeki64(mFile, current_position, SEEK_SET);

        return mFileSize;
    }

    int64 Read64() { return _Read<int64>(); }
    int32 Read32() { return _Read<int32>(); }
    int16 Read16() { return _Read<int16>(); }
    int8 Read8() { return _Read<int8>(); }
    float ReadFloat() { return _Read<float>(); }
    double ReadDouble() { return _Read<double>(); }

    void Skip(int64 bytes) { _lseeki64(mFile, bytes, SEEK_CUR); }
    void Seek(int64 pos) { _lseeki64(mFile, pos, SEEK_SET); }
    int64 Tell() { return _telli64(mFile); }

    template <class T>
    T Read() { return _Read<T>(); }

    void Read(char *to, size_t size) {
        const int ret = read(mFile, (void *)to, size);
        if ((int)size != ret)
            FATAL(format("Read error: attempted to read %d bytes, read() returned %d, errno: %s [we are at offset %d, file size is %d]") % size % ret % strerror(errno) % Tell() % GetSize());
    }

    template <class T>
    BinaryIFile& operator>>(T& val) { val = _Read<T>(); return *this; }

private:
    const string mPath;
    int mFile;
    int64 mFileSize;

    template <class T>
    T _Read() { T ret; if (sizeof(ret) != read(mFile, (void *)&ret, sizeof(ret))) FATAL("Read error"); return ret; }
};

【问题讨论】:

  • 现在真正的问题变成了:如何在 Windows 上获得 64 位文件位置(除了切换到 64 位操作系统)。抱歉,我帮不上忙。
  • 是的,那是你的问题。带有一个参数的seekg 带有一个streampos 参数,带有两个参数的seekg 带有一个streamoff 参数。后者必须签名。

标签: c++ windows iostream seekg


【解决方案1】:

你可以在给定位置之前寻找,所以 pos 是有符号的。尝试使用大小为 0x7fffffff 和 0x80ffffff 的文件,看看后者是否会触发问题,这是我的猜测。

【讨论】:

  • 哇,谢谢,我没想到!你知道是否有 64 位搜索的功能吗?我知道在 C、seek64 或类似(非标准)中有,但是 C++ - Windows 呢?
  • 在 C++ 中,64 位版本可以(并且可能会)通过重载实现,因此您可以先尝试使用 64 位类型,看看您的实现是否提供它们。
  • 不是 Windows 专家,但我猜您将不得不使用他们的操作系统特定界面。他们有大量的在线文档。
  • @jam:不起作用。无论如何都不需要重载,因为 int 会被隐式转换为 int64,所以只需要存在 int64 函数
  • 无论如何,我刚刚决定我将为此使用 C 库并使用 lseek64 / _lseeki64。感谢保罗的帮助! (我也可以从最后开始否定,但我想支持大于 4 GB 的文件)