seekg не может правильно обработать файл размером 4294967295 байт

Я обнаружил, что в VS2010 функция seekg не работает должным образом, когда открывается файл размером ровно 4294967295 байт.

Я использую простой код:

#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-битными !? Я понятия не имею ))

9

Решение

Это действительно ошибка в Visual C ++ 2010. Об этом сообщалось в Microsoft Connect два года назад: «std::fstream использовать 32-битный int как pos_type даже на платформе x64 « (название ошибки неверно; симптомы были на самом деле вызваны этой ошибкой в _FPOSOFF).

Эта ошибка исправлена ​​в Visual C ++ 2012, где _FPOSOFF определяется как:

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

Если вы это сделаете, вам будет рекомендовано перейти на Visual C ++ 2012.

5

Другие решения

Внутренне реализация потока имеет константу _BADOFF, равную 0xffffffff, которая возвращается, когда поиск завершился неудачно. В этом случае поиск завершается успешно, но возвращаемое значение из поиска равно коду сбоя, что приводит к тому, что обертка потока ошибочно устанавливает свой код ошибки.

_BADOFF определен как 64-битный тип, ему просто присвоено глупое значение.

Как обходной путь, вы можете искать 1-байтовый короткий, а затем прочитать байт.

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

Однако обратите внимание, что эта ошибка будет проявляться каждый раз, когда требуется поиск конкретного смещения файла, поэтому она может все еще быть проблемой для больших файлов, если вы будете искать их в случайных местах. Например, если ваш файл был на один байт больше, поиск -1 завершился бы неудачей, так что это не общее решение.

ОП расширила свой вопрос, поэтому я расширю свой ответ. Да, значение поиска приведено с использованием небезопасного преобразования перед сравнением. Похоже, это не влияет на возможность поиска за пределами этой точки в файле, так как он используется только для сравнения со значением ошибки — поток все еще имеет правое смещение. Однако, по-видимому, это является основной причиной того, что _BADOFF является сомнительным в первую очередь, так как _BADOFF имеет значение «-1» в источнике и будет подвергаться тому же преобразованию, усекая до 0xffffffff.

Таким образом, исправление для libs может состоять в том, чтобы исправить приведение (при условии, что нет никаких других побочных эффектов от этого), но ради решения проблемы, вам нужно только избегать поиска позиций, где нижние 32-битные установлены. Это будет искать за пределами этого ОК, из того, что я вижу.

7

На данный момент есть два не очень приятных решения

  1. Сделайте исправление в stdio.h

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

  2. Переход на VS2012 (что тоже печально)

    Эта ошибка исправлена ​​в Visual Studio 2012: _FPOSOFF теперь приводит к длинному длинному и, таким образом,> избегает усечения. — Джеймс МакНеллис

1
По вопросам рекламы [email protected]