Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Фича в стандартной библиотеке С++
Форум на CrossPlatform.RU > Разработка > С\С++
Iron Bug
В общем, напоролась я на фичу (как оказалось, а не багу), потратила кучу времени. Хочу предупредить всех, что она есть.
Проблема в том, что в стандартном ifstream, после чтения до конца файла (eof()), функция seekg работать перестаёт.
Вот простой код, который даёт неравные буферы:

// TestIfstreamSeekg.cpp 

#include <fstream>
#include <iostream>

using namespace std;

#define BUFFER_SIZE 10

int main(int argc, char* argv[])
{
    ifstream ifs("TestIfStreamSeekg.exe",ios::binary); // любой файл, длиной больше 10 байт, для простоты взят сам exe-шник, формат открытия неважен, может быть и текстовый

    // читаем первые байты
    char buffer0[BUFFER_SIZE];
    ifs.read(buffer0,BUFFER_SIZE);

    // доходим до eof()
    char temp[1000];
    while(!ifs.eof())
    {
        ifs.read(temp,1000);
    }
      
    //ifs.clear(); // а это волшебный финт ушами, который при раскомментировании помогает заставить seekg работать (шаманство подсмотрено на других форумах)
    // перемещаемся к началу файла (якобы)
    ifs.seekg(0,ios::beg);

    // снова читаем первые байты
    char buffer1[BUFFER_SIZE];
    ifs.read(buffer1,BUFFER_SIZE);
  
    // сравниваем считанные буферы
    if(memcmp(buffer0,buffer1,BUFFER_SIZE) != 0)
    {
        // опа! буферы разные
        cout << "buffers are not equal!" << endl;
    }
    else
    {
        // всё зашибись!
        cout << "buffers are equal" << endl;
    }
    ifs.close();
    return 0;
}


решение проблемы - сброс бита: перед вызовом seekg() вызывать clear(). можно ещё переоткрыть файл - тоже, естественно, сработает.
главное, знать об этом косяке :)
ssoft
Дак вроде так везде работает, не только под MSVS.

При достижении конца выставляется статус eofbit (тип iostate), аля кутешного ReadPastEnd.
Пока статус не изменится с помощью clear() на goodbit (в Qt resetStatus() изменяет на Ok) ничего с потоком сделать нельзя, в том числе и вернуться в начало.
Iron Bug
Цитата(ssoft @ 5.10.2012, 18:21) *
Дак вроде так везде работает, не только под MSVS.

При достижении конца выставляется статус eofbit (тип iostate), аля кутешного ReadPastEnd.
Пока статус не изменится с помощью clear() на goodbit (в Qt resetStatus() изменяет на Ok) ничего с потоком сделать нельзя, в том числе и вернуться в начало.

действительно. я дома под линём проверила только что.
значит, просто я столько лет не знала о такой странной фиче. видимо, никогда не пыталась перемещаться по файлу после прочтения его до конца, а тут понадобилось.
и всё же, почему eof считается "ошибкой" - мне непонятно. обычный статус потока, почему его нужно обрабатывать как-то отдельно - полная загадка.

ладно, переименую тему, чтобы было более адекватно.
Litkevich Yuriy
Да бага это однозначно, просто авторы отмазываются.

Если некая функция позволяет устанавливать указатель куда-нибудь, значит он должен безоговорочно устанавливаться.

Единственное место в котором бессмысленна установка указателя - последовательные интерфейсы.
Iron Bug
Цитата(Litkevich Yuriy @ 7.10.2012, 17:05) *
Да бага это однозначно, просто авторы отмазываются.

Если некая функция позволяет устанавливать указатель куда-нибудь, значит он должен безоговорочно устанавливаться.

Единственное место в котором бессмысленна установка указателя - последовательные интерфейсы.

ну вот как бы и я так же думала: раз это файл, то ползать по нему можно туда-сюда без ограничений. а оказалось, что eof его переводит в какое-то особое невменяемое состояние и надо ещё отслеживать, был ли этот eof. с моей точки зрения, это маразм, конечно. если я на начало файла перевожу указатель чтения, то какая мне разница, был он в конце или в середине... тем не менее, вот так вот реализовано. причём везде.
Влад
Это не отмазка, а точное следование требованию Стандарта языка.
Бит eofbit устанавливается в момент попытки чтения за концом файла - естественно, попытка эта неудачная; и поэтому одновременно устанавливается failbit. В соответствии с буквой Стандарта (27.7.2.3, cl.41,43), операция seekg() игнорируется до тех пор, пока не будет сброшен failbit.
Iron Bug
просто стандарт слишком древний. он не соответствует нормальной логике потоков со свободным доступом :) это типичный лишний вызов. если я хочу переместиться а абсолютный старт потока со свободным доступом - это всегда возможно, где бы ни находился указатель. возможно, стандарт писали, когда файлы хранились на каких-нибудь первобытных устройствах типа лент.
в общем, проще написать обёртку, которая перегружает это дело, проверяет и сбрасывает этот никому не нужный бит, юзать её и не париться.
Влад
Хмм... Называть 14882-2011 "слишком древним" я бы, пожалуй, не стал бы... :-)
Iron Bug
Цитата(Влад @ 10.10.2012, 0:01) *
Хмм... Называть 14882-2011 "слишком древним" я бы, пожалуй, не стал бы... :-)

дык, это ж не в новом варианте появилось. это рудимент от старых времён остался. просто забыли пересмотреть.
Влад
Может, забыли, а может - нет.... Мы не знаем всех соображений, которыми руководствовался Комитет (а работают там отнюдь не глупые люди).
Я бы предложил тебе написать письмо с обоснованием в WG21 - они довольно объективно принимают предложения по совершенствованию Стандарта. Я допускаю, что были какие-то основания написать в Стандарте именно так, а не иначе, - какие именно, будет ясно из ответного письма. Из известных мне случаев (писал предложения не я), ответ приходит всегда, - даже если это вежливый отказ. По крайней мере, если твое предложение не будет принято, мы через пару месяцев будем четко знать, почему Стандарт требует делать так, а не иначе.
Iron Bug
я думаю, что весь вопрос в том, что понимать под словом "файл". однако, я не могу представить себе ни одного девайса, который бы хранил "файлы" с однонаправленным доступом. сейчас такого просто нет в природе. по крайней мере, на начало точно всегда можно перейти без всякого ущерба.
но я сильно подозреваю, что всё это старьё будет заменено на то, что сейчас сделано в boost::Iostreams. там очень чётко разделены потоки однонаправленные и потоки с произвольным доступом. вообще, у них там концепция очень хорошо продумана и я пока буду юзать их библиотеки, чтобы не париться.
писать или не писать - подумаю. спамить я не люблю, тем более, по такому мелкому поводу. думаю, у комитета есть проблемы поважнее. вон, Страуструп много чего предлагал, но его послали лесом (пока что) :) буст всё равно постепенно переходит в стандарт. надеюсь, что и с потоками будет то же самое.
AD
Цитата(Iron Bug @ 10.10.2012, 18:40) *
я думаю, что весь вопрос в том, что понимать под словом "файл". однако, я не могу представить себе ни одного девайса, который бы хранил "файлы" с однонаправленным доступом. сейчас такого просто нет в природе. по крайней мере, на начало точно всегда можно перейти без всякого ущерба.

А как так называемые pipe??? Или я неправильно понял фразу про "девайсы" с однонаправленным доступом.
Iron Bug
Цитата(AD @ 11.10.2012, 13:44) *
А как так называемые pipe

пайп - это пайп. это не файл. файл - объект с хранением. а пайп - это просто поток данных.
и для пайпов в венде есть отдельные функции. хоть и ракообразные, но есть.
Litkevich Yuriy
мне помнится, что я на билдере делал кольцевой буфер-файл. И писалось там примитивно - дошёл до края файла, подвинул указатель в начало. Не помню, чтоб я там как-то изголялся.

А в стандарте могли оставить из соображения совместимости, вдруг имеющиеся программы глючить начнут. Стандарты штука тонкая.
AD
Цитата(Iron Bug @ 12.10.2012, 7:54) *
пайп - это пайп. это не файл. файл - объект с хранением. а пайп - это просто поток данных.
и для пайпов в венде есть отдельные функции. хоть и ракообразные, но есть.

Причем здесь windows? Для Linux PIPE - это такой же файл, как и сокет, кстати. Насколько я знаю указанные "девайсы": сокеты, pipe и являются однонаправленными.
Litkevich Yuriy
Цитата(AD @ 12.10.2012, 12:03) *
Для Linux PIPE - это такой же файл
да в линухе всё файл. даже диспетчер задач не нужен, зайди в каталог proc. Но не ко всем файлам в линухе можно произвольно обращаться
AD
Цитата(Litkevich Yuriy @ 12.10.2012, 15:21) *
Но не ко всем файлам в линухе можно произвольно обращаться

О том и речь, что однонаправленные устройства не являются устаревшим понятием! Разве через iostream нельзя обращаться к PIPE???
Litkevich Yuriy
AD, она не про пайпы, а про файлы:
Цитата(Iron Bug @ 10.10.2012, 20:40) *
я не могу представить себе ни одного девайса, который бы хранил "файлы" с однонаправленным доступом.



Т.е. если устройство поддерживает понятие файл, то оно поддерживает его в полной мере - читать, писать, создавать и удалять. И доступ к этим файлам произвольный (с любого места в файле)
AD
Цитата(Litkevich Yuriy @ 12.10.2012, 16:14) *
AD, она не про пайпы, а про файлы:
Цитата(Iron Bug @ 10.10.2012, 20:40) *
я не могу представить себе ни одного девайса, который бы хранил "файлы" с однонаправленным доступом.


Еще раз... Если в linux pipe, сокет - это "файлы", то почему их нельзя назвать "файлами" с однонаправленным доступом? Или я не так понял эту фразу про однонаправленность?
Litkevich Yuriy
изначально речь не шла вообще о потоках и прочем, только о объектах файловой системы с произвольным доступом. А ты тему в сторону увёл.
AD
Цитата(Litkevich Yuriy @ 12.10.2012, 16:19) *
изначально речь не шла вообще о потоках и прочем, только о объектах файловой системы с произвольным доступом. А ты тему в сторону увёл.

Ну вот теперь я понял, что речь шла о так называемых "обыкновенных файлах", как они названы в вики.

Умный человек подсказал, что в iostream есть возможность работы с PIPE. Вот точная цитата:
Цитата
У Джосаттиса в его The C++ Standard Library 2nd Ed. приведен пример (p.835)
...
хитрожопо, но возможно
....
т.е. технически возможно открыть пайп или сокет, присоединить его к потоку-наследнику istream и читать из него. Но вот возможности отката не будет....
Iron Bug
да, iostream от fstream существенно отличается, однако :) не о том речь, в общем.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2024 IPS, Inc.