Когда `ifstream :: readsome` устанавливает` eofbit`? - PullRequest
9 голосов
/ 08 февраля 2012

Этот код зацикливается навсегда:

#include <iostream>
#include <fstream>
#include <sstream>

int main(int argc, char *argv[])
{
    std::ifstream f(argv[1]);
    std::ostringstream ostr;

    while(f && !f.eof())
    {
        char b[5000];
        std::size_t read = f.readsome(b, sizeof b);
        std::cerr << "Read: " << read << " bytes" << std::endl;
        ostr.write(b, read);
    }
}

Это потому, что readsome никогда не устанавливает eofbit.

cplusplus.com говорит:

Ошибки сообщаются путем изменения внутренних флагов состояния:

eofbit При вызове функции указатель get находится в конце внутреннего входного массива буфера потока, что означает отсутствие позиций.быть прочитанным во внутреннем буфере (который может быть или не быть концом входной последовательности).Это происходит, когда rdbuf()->in_avail() вернет -1 до извлечения первого символа.

failbit Поток находился в конце источника символов до вызова функции.

badbit Произошла ошибка, отличная от описанной выше.

Почти то же самое, стандарт гласит:

[C++11: 27.7.2.3]: streamsize readsome(char_type* s, streamsize n);

32.Эффекты: ведет себя как неотформатированная функция ввода (как описано в пункте 27.7.2.3, параграф 1).После создания объекта-часового, если !good() вызывает setstate(failbit), который может вызвать исключение и вернуться.В противном случае извлекает символы и сохраняет их в последовательных местах массива, первый элемент которого обозначен s.Если rdbuf()->in_avail() == -1, вызывает setstate(eofbit) (что может выдать ios_base::failure (27.5.5.4)) и не извлекает никаких символов;

  • Если rdbuf()->in_avail() == 0, не извлекает никаких символов
  • Если rdbuf()->in_avail() > 0, извлекается min(rdbuf()->in_avail(),n)).

33.Возвращает: Количество извлеченных символов.

То, что условие in_avail() == 0 является неактивным, подразумевает, что ifstream::readsome само по себе является недопустимым, если буфер потока пуст, ноусловие in_avail() == -1 подразумевает, что оно будет устанавливать eofbit, когда какая-то другая операция привела к in_avail() == -1.

Это выглядит как несоответствие, даже несмотря на то, что«некоторая» природа readsome.

Так что же является семантикой readsome и eof?Правильно ли я их истолковал?Являются ли они примером плохого дизайна в библиотеке потоков?


(Украдено из [IMO] недействительно libstdc ++ ошибка 52169 .)

Ответы [ 4 ]

4 голосов
/ 15 февраля 2012

Я думаю, что это точка настройки, которая не используется реализацией потоков по умолчанию.

in_avail() возвращает количество символов, которые оно может видеть во внутреннем буфере, если оно есть. В противном случае он вызывает showmanyc(), чтобы попытаться определить, известны ли символы, доступные в другом месте, поэтому запрос на заполнение буфера гарантированно будет успешным.

В свою очередь, showmanyc() вернет число известных ему символов, если оно есть, или -1, если знает , что чтение не удастся, или 0, если не имеет подсказки .

Реализация по умолчанию (basic_streambuf) всегда возвращает 0, так что это то, что вы получите, если у вас нет потока с каким-либо другим переопределением streambuf showmanyc.

Ваш цикл, по сути, читается как многие символы, как вы знаете, безопасен, и застревает, когда он равен нулю (что означает "не уверен").

1 голос
/ 30 апреля 2018

Другие ответили, почему readsome не установит eofbit по замыслу. Я предложу способ чтения некоторых байтов до eof без установки бита fail интуитивно, так же, как вы пытались использовать readsome. Это результат исследования другого вопроса .

#include <iostream>
#include <fstream>
#include <sstream>

using namespace std;

streamsize Read(istream &stream, char *buffer, streamsize count)
{
    // This consistently fails on gcc (linux) 4.8.1 with failbit set on read
    // failure. This apparently never fails on VS2010 and VS2013 (Windows 7)
    streamsize reads = stream.rdbuf()->sgetn(buffer, count);

    // This rarely sets failbit on VS2010 and VS2013 (Windows 7) on read
    // failure of the previous sgetn()
    stream.rdstate();

    // On gcc (linux) 4.8.1 and VS2010/VS2013 (Windows 7) this consistently
    // sets eofbit when stream is EOF for the conseguences  of sgetn(). It
    // should also throw if exceptions are set, or return on the contrary,
    // and previous rdstate() restored a failbit on Windows. On Windows most
    // of the times it sets eofbit even on real read failure
    stream.peek();

    return reads;
}

int main(int argc, char *argv[])
{
    ifstream instream("filepath", ios_base::in | ios_base::binary);
    while (!instream.eof())
    {
        char buffer[0x4000];
        size_t read = Read(instream, buffer, sizeof(buffer));
        // Do something with buffer 
    }
}
1 голос
/ 21 февраля 2012

Я не думаю, что readsome () предназначен для того, что вы пытаетесь сделать (читать из файла на диске) ... cplusplus.com:

Функцияпредназначен для чтения двоичных данных из определенных типов асинхронных источников, которые могут ожидать больше символов, так как он останавливает чтение при исчерпании локального буфера, избегая потенциальных непредвиденных задержек.

Так что это звучит как чтение() предназначен для потоков из сетевого сокета или чего-то подобного, и вы, вероятно, хотите просто использовать read ().

1 голос
/ 08 февраля 2012

Если символ недоступен (т. Е. gptr() == egptr() для std:streambuf), вызывается функция виртуального члена showhowmanyc(). Я мог бы иметь реализацию showmanyc(), которая возвращает код ошибки. Почему это может быть полезно, другой вопрос. Тем не менее, это может установить eof(). Конечно, in_avail() предназначено не для сбоя и не для блокировки, а просто для возврата символов, о которых известно, что они доступны. То есть цикл, который у вас есть, по сути гарантированно будет бесконечным, если у вас нет довольно странного потокового буфера.

...