Нужно узнать размер файла (как еще кстати это сделать не прибегая к системному API и boost::filesystem, ну кроме банального fopen() и т.д.) ?
Вот такой код:
Если открывать файл размером 10 gb вернёт 0 т.е. сработает условие !in.good(), если убрать std::ifstream::ate файл откроется.
Можно посикать до конца и тогда in.tellg() — вернёт правильный размер.
Интересно почему с std::ifstream::ate на файлах >= 10Gb не работает. Бага? (Если да куда писать?)
Здравствуйте, nen777w, Вы писали:
N>Если открывать файл размером 10 gb вернёт 0 т.е. сработает условие !in.good(), если убрать std::ifstream::ate файл откроется. N>Можно посикать до конца и тогда in.tellg() — вернёт правильный размер. N>Интересно почему с std::ifstream::ate на файлах >= 10Gb не работает. Бага? (Если да куда писать?)
32 bit переполнение. По всей видимости size_t используется. Попробуй в 64-bit собрать.
Здравствуйте, nen777w, Вы писали:
N>Нужно узнать размер файла (как еще кстати это сделать не прибегая к системному API и boost::filesystem, ну кроме банального fopen() и т.д.) ?
размер файла обычно узнают как раз через системное АПИ, через мета-данные, а не через открытие содержимого файла
в плюсах крайне скудная поддержка файлового АПИ
N>>Если открывать файл размером 10 gb вернёт 0 т.е. сработает условие !in.good(), если убрать std::ifstream::ate файл откроется. N>>Можно посикать до конца и тогда in.tellg() — вернёт правильный размер. N>>Интересно почему с std::ifstream::ate на файлах >= 10Gb не работает. Бага? (Если да куда писать?)
CS>32 bit переполнение. По всей видимости size_t используется. Попробуй в 64-bit собрать.
Я не копал вглубь, времени в нет, но приложение на котором это обнаружилось 64-битное.
Ну и не может быть (не должно по крайней мере) так чтоб для фиг знает сколько существуюшего NTFS в реализации (i/o)fstream ограничились 32 битными типами.
Потом почему если открывть а потом сикать то работает. tellg() возвращает 64-битный беззнаковый.
Бага скорее всего.
Во общем постараюсь найти время сегодня-завтра пойду в глубь посмотрю что там происходит, потом отпишусь.
N>>Нужно узнать размер файла (как еще кстати это сделать не прибегая к системному API и boost::filesystem, ну кроме банального fopen() и т.д.) ?
U>размер файла обычно узнают как раз через системное АПИ, через мета-данные, а не через открытие содержимого файла U>в плюсах крайне скудная поддержка файлового АПИ
Да я в курсе что API надёжнее всего. Просто думал может пропустл что (они ж с boost что то тащат постоянно в std) а потом вспомнил, что только недавно обсуждалось тут появление filesystem в c++14.
Теоретически, использование stream.tellg() — это непереносимый способ узнать размер файла. Так как tellg() возвращает лишь некое положение в потоке данных, которое в дальнейшем может быть использовано чтобы вернуться к этому положению.
Единственный способ узнать размер потока в С++ используя IOStreams — это прочитать весь поток до конца, проигнорировав все данные и потом узнать кол-во прочитанных байт:
file.ignore( std::numeric_limits<std::streamsize>::max() );
std::streamsize length = file.gcount();
file.clear(); // Since ignore will have set eof.
file.seekg( 0, std::ios_base::beg );
PM>Теоретически, использование stream.tellg() — это непереносимый способ узнать размер файла. Так как tellg() возвращает лишь некое положение в потоке данных, которое в дальнейшем может быть использовано чтобы вернуться к этому положению.
PM>Единственный способ узнать размер потока в С++ используя IOStreams — это прочитать весь поток до конца, проигнорировав все данные и потом узнать кол-во прочитанных байт:
[skip] PM>Источник: http://stackoverflow.com/a/22986486/1355844
Спасибо.
PM>Самый надежный вариант — использовать API целевой платформы, ну или boost::filesystem для большинства распространенных платформ.
Ладно. Но суть не в том что бы узнать размер файла.
Суть в этом:
std::ifstream in(path, std::ifstream::ate|std::ifstream::binary);
if(!in.good()) {
//WTF? для файлов больше 10Gb
}
Уже давайте по чесноку что бы вообще, (хотя ИМХО оно не должно от этого зависеть) — приложение 64-битное.
Пошел копать...
Стрим открывается, потом ему делают:
if (!atendflag || fseek(fp, 0, SEEK_END) == 0)
return (fp); // no need to seek to end, or seek succeeded
fclose(fp); // can't position at endreturn (0);
Так вот fseek() не вернул 0.
Копаем в fseek() последнее что там делают куда без дизассемблирования не докопаться это в long __cdecl _lseek_nolock()
std::ifstream in(path, /*std::ifstream::ate|*/std::ifstream::binary);
if(!in.good()) {
return 0;
}
in.seekg(0, std::ifstream::end);
//и еще разif(!in.good()) {
return 0;
}
все ок!
Копаем:
Опа... а в конце то все дело теперь делает: __int64 __cdecl _lseeki64_nolock()
И уже знакомый код, только теперь он выглядит вот так и всю работу теперь делает SetFilePointerEx:
Параметры всё те же, только заиспользовали LARGE_INTEGER newpos;
if ((osHandle = (HANDLE)_get_osfhandle(fh)) == (HANDLE)-1)
{
errno = EBADF;
_ASSERTE(("Invalid file descriptor. File possibly closed by a different thread",0));
return( -1i64 );
}
if (!SetFilePointerEx( osHandle,
*(PLARGE_INTEGER)&pos,
&newpos,
mthd) )
{
_dosmaperr( GetLastError() );
return( -1i64 );
}
newpos.QuadPart будет содержать то что и ожидалось 10737418240 т.е. длинну нашего 10Gb файла.
Ну вот так вот... у меня пока всё.
Но судя по всему звать нужно было SetFilePointerEx() т.к. SetFilePointer() оперирует с LONG а вот SetFilePointerEx() LARGE_INTEGER
[здесь было расследование] N>Ну вот так вот... у меня пока всё. N>Но судя по всему звать нужно было SetFilePointerEx() т.к. SetFilePointer() оперирует с LONG а вот SetFilePointerEx() LARGE_INTEGER
N>Бага?
Может быть. В VS2013 при открытии файла в режиме append, вызывается int fseek(file, 0, END) в недрах которого вызывается long _lseek() и в конечном итоге SetFilePointerEx(), но для больших файлов это не помогает, т.к. long у MS всегда 32 бита, даже в 64-битной программе. Поэтому внутри реализации _lseek такой код:
/* Try to set the new file pointer */
large_pos.QuadPart = pos;
if (!SetFilePointerEx(osHandle, large_pos, &new_pos, mthd)) {
_dosmaperr(GetLastError());
return -1;
}
/* The call succeeded, but the new file pointer location is
too large for the return type or a negative value.
So, restore file pointer to saved location and return error.
*/if (new_pos.HighPart != 0) {
/* */
SetFilePointerEx(osHandle, saved_pos, NULL, FILE_BEGIN);
errno = EINVAL;
return -1;
}
Т.е. fseek() всегда возвращает ошибку при смещении больше чем на 4Gb.
Мораль? Пользуйтесь функциями платформы для получения размера файла