istream_iterator пытается проанализировать недопустимые данные

Привет, я надеялся, что кто-то поможет понять это поведение приведенного ниже кода.

#include <iostream>
#include <algorithm>
#include <string>
#include <limits>
#include <fstream>
#include <iterator>
#include <stdexcept>


struct asound_stanza
{
    unsigned index;
    std::string name;

    friend std::istream &operator>>(std::istream &is, asound_stanza &stan)
    {
        is >> stan.index;
        if(!is.good())
            return is;
        std::getline(is, stan.name);
        std::string::const_iterator
            eol   = stan.name.cend(),
            start = std::find(stan.name.cbegin(), eol, '['),
            end   = std::find(start, eol, ' ');
        stan.name = std::string(++start, end);
        is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        // std::istream_iterator<char> it;
        // while(*it++!=0x0a);
        return is;
    }
};

int main()
{
    std::ifstream is("/proc/asound/cards", std::ifstream::in); 
    std::istream_iterator<asound_stanza> it(is), end;
    unsigned devid = 0;
    std::for_each(it, end, [&](const asound_stanza &stan)->void{
        if(stan.name!="CODEC")
            return;
        devid = stan.index;
    });
    std::cout << devid;
    return 0;
}

это работает, но у меня есть несколько вопросов. после того, как все допустимые итерации выполнены, он идет вперед и пытается разобрать еще один, который неизбежно терпит неудачу (отсюда if(!is.good())..._). Он пытается разобрать его, но никогда не передает окончательную искаженную структуру в лямбда-выражение. Почему нет? Это потому, что потоки нехорошие (), поэтому он не пытается передать их?

Кроме того, как я могу заставить его даже не пытаться проанализировать окончательную структуру (каждая строфа заканчивается новой строкой (0x0a), поэтому я бы подумал, что игнорирование потока до новой строки вызовет EOF на последней действительной итерации но это не так.

Спасибо за ваше руководство.

Также не стесняйтесь передавать другие комментарии о правильности кодирования.

PS: мой файл выглядит так

0 [Intel          ]: HDA-Intel - HDA Intel
                     HDA Intel at 0xfc500000 irq 46
1 [HDMI           ]: HDA-Intel - HDA ATI HDMI
                     HDA ATI HDMI at 0xfc010000 irq 47
2 [CODEC          ]: USB-Audio - USB Audio CODEC
                     Burr-Brown from TI USB Audio CODEC at usb-0000:00:1d.7-3.1, full speed

person 111111    schedule 17.08.2011    source источник


Ответы (2)


Если поток находится в недопустимом состоянии или если попытка чтения из потока внутри оператора >> не удалась и устанавливает поток в недопустимое состояние, istream_iterator устанавливает себя в положение конца потока. Таким образом, итерация заканчивается, даже не взглянув на частично проанализированный объект.

person Tomek Szpakowicz    schedule 17.08.2011
comment
Спасибо, что ответили на один из моих вопросов о том, почему он отображает такое поведение, однако мне бы хотелось знать, почему ignore(...max(), '\n'); не устанавливает флаг EOF. - person 111111; 17.08.2011
comment
@ 111111: Игнор читает upto EOF (но не прошлое). Таким образом, он может читать все символы из потока. Это всегда первая операция, которая пытается прочитать за конец файла, которая терпит неудачу (или устанавливает флаг EOF). Примечание. Последняя удачная операция будет считываться до eof. - person Martin York; 18.08.2011

Ваш код должен выглядеть так:

friend std::istream &operator>>(std::istream &is, asound_stanza &stan)
{
    if ( !(is >> stan.index) )
        return is;  //return if couldn't read unsigned int

    if(std::getline(is, stan.name))//go inside ONLY IF read is successful
    {
       std::string::const_iterator 
       eol  =stan.name.cend(),
       start=std::find(stan.name.cbegin(), eol, '['),
       end  =std::find(start, eol, ' ');
       stan.name=std::string(++start, end);    
       is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }
    return is;
}

Читайте комментарии в моей модификации выше!

Поскольку это обсуждалось так много раз, и я уже много раз отвечал, я не хочу повторять это снова. Пожалуйста, прочитайте мой другой ответ, который объясняет саму вещь:

person Nawaz    schedule 17.08.2011
comment
Спасибо, это более приятный способ, но я хотел бы знать, ПОЧЕМУ он не передает искаженную строфу лямбде и почему он даже пытается разобрать последнюю, когда игнор обязательно должен установить EOF? - person 111111; 17.08.2011
comment
@ 111111: Смотрите мой ответ еще раз. Я объяснял это раньше, поэтому прочитайте мой другой ответ. - person Nawaz; 17.08.2011
comment
Вышеупомянутое неплохое утверждение. Но лично я бы объединил два чтения в одно if. if ((is >> stan.index) && (std::getline(is, stan.name)) { /* We got the object */} - person Martin York; 18.08.2011
comment
@Мартин, вот как сейчас - person 111111; 18.08.2011