Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Как правильно читать из istream неизвестное?


Автор: Dims 29.11.2007, 13:08
Допустим, я читаю некоторый файл.

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

Один из типов -- это CDataPoint. Его текстовое представление являет собой перечень чисел через запятую.

Я не могу написать для него тупой оператор 

Код

istream& operator>>(istream& is, CDataPoint& dp);


потому, что я не знаю, сколько элементов будет в строке (число чисел разное).

Ладно, здесь я могу выкрутиться. Я могу считывать элементы поштучно в какой-то массив, анализировать не прочитан ли конец строки и, когда прочитан, зная число элементов, перекладывать их по членам класса. Некрасиво, но работать будет.

Однако, ситуация осложняется тем, что в строке может содержаться вообще что-то другое. Например, неправильно составленный CDataPoint или комментарий. Комментарий отличается тем, что в первой позиции находится символ комментария. Ладно, тут можно прочитать один символ, проверить его и вернуть назад в поток. А как быть со случаем, когда встретится что-то другое? Мне нужно, чтобы программа не прерывалась, а чтобы неудачный кусок потока просто был бы обработан иначе. 

Как правильно поступить?

Единственное разумное решение, которое мне видится, это читать файл в виде текстовых строк и потом их разбирать. Но это получается, что система istream-ов не используется. Можно ли как-то ей воспользоваться тут? Например, можно ли поставить в потоке какую-то закладку, а потом, при неудаче, откатить поток к этой закладке?

Автор: xvr 29.11.2007, 13:33
Цитата(Dims @ 29.11.2007,  13:08)
Допустим, я читаю некоторый файл.

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

Это автоматически означает, что прямое чтение из файла в переменные этих типов не пройдет - неизвестно куда надо читать. Т.е. надо предварительно считать данные, определить их тип, и только потом стримить в соотвествующую переменную.
Цитата

Один из типов -- это CDataPoint. Его текстовое представление являет собой перечень чисел через запятую.

Я не могу написать для него тупой оператор 

Код

istream& operator>>(istream& is, CDataPoint& dp);


потому, что я не знаю, сколько элементов будет в строке (число чисел разное).

Ладно, здесь я могу выкрутиться. Я могу считывать элементы поштучно в какой-то массив, анализировать не прочитан ли конец строки и, когда прочитан, зная число элементов, перекладывать их по членам класса. Некрасиво, но работать будет.

Есть 2 класса данных: лексемы и грамматики. Лексема может быть описанна регулярным выражением, а для считывания такой лексемы достаточно иметь 1 lookahead символ, его потом можно вернуть в поток. В подавляющем большинстве случаев регулярное выражение, описывающее лексему, достаточно простое и ее чтение из потока кодируется элементарно (пример - целое число)

С грамматиками сложнее. Они описываются не регулярными выражениями, а конечными автоматами со стековой памятью, и в качестве входных элементов у них выступают лексемы. В принципе, для чтения грамматики также достаточно 1 lookahead символа (только на это  раз это будет лексема), а чтение из потока по такой грамматике сложнее, чем чтение лексем. Для такого чтения нужно применять либо явный автомат, либо его воплощение в виде набора рекурсивных процедур (метод рекурсивного спуска)

Цитата

Однако, ситуация осложняется тем, что в строке может содержаться вообще что-то другое. Например, неправильно составленный CDataPoint или комментарий. Комментарий отличается тем, что в первой позиции находится символ комментария. Ладно, тут можно прочитать один символ, проверить его и вернуть назад в поток. А как быть со случаем, когда встретится что-то другое? Мне нужно, чтобы программа не прерывалась, а чтобы неудачный кусок потока просто был бы обработан иначе. 
Как правильно поступить?

Классическая грамматика. Тебе нужен парсер на ВСЮ грамматику целиком.

Цитата

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

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

Цитата

Но это получается, что система istream-ов не используется. Можно ли как-то ей воспользоваться тут? 

Можно, читай из istream'а в строку и разбирай smile

Цитата

Например, можно ли поставить в потоке какую-то закладку, а потом, при неудаче, откатить поток к этой закладке?

Можно, tellg/seekg - но многократное позиционирование потока будет хуже, чем предварительное чтение в строку.

Автор: Dims 29.11.2007, 15:07
Цитата(xvr @  29.11.2007,  13:33 Найти цитируемый пост)
Можно, tellg/seekg - но многократное позиционирование потока будет хуже, чем предварительное чтение в строку.

А разве там нет буфера? Или буфер не поможет в отмотке назад?

Да, как-то не кузяво.

Автор: archimed7592 29.11.2007, 15:57
Цитата(Dims @  29.11.2007,  15:07 Найти цитируемый пост)
Да, как-то не кузяво. 

Есть такие прекрасные штуки, как XML и сериализация. В XML и комментарии делать можно, и десериализация не так сложна, как в случае своих велосипедов.

Добавлено через 15 секунд
см. boost::serialization.

Автор: Dims 29.11.2007, 16:07
У меня файл, за формат которого я не отвечаю. То есть, мне нужно прочитать. Собственно, я его уже читаю обычным scanf-ом, но подумал, не сделать ли новыми средствами...

А вообще, если сузить вопрос.

Что положено делать в переопределённом операторе operator>> в случае, если при чтении обнаруживается, что формат данных не соблюдён? Исключение выкидывать?

Автор: archimed7592 29.11.2007, 16:46
bad_bit выставлять должно и, если istream.exceptions() != 0, то, тогда перебрасывать исключение(но, тогда его и ловить нужно, ибо поток сам кинуть может)...

Добавлено через 6 минут и 36 секунд
Но, боюсь, тебе это не поможет, ибо ни bad_bit, ни исключение обратно данные в поток не засунет...

Автор: xvr 29.11.2007, 18:11
Цитата(Dims @ 29.11.2007,  15:07)
Цитата(xvr @  29.11.2007,  13:33 Найти цитируемый пост)
Можно, tellg/seekg - но многократное позиционирование потока будет хуже, чем предварительное чтение в строку.

А разве там нет буфера? Или буфер не поможет в отмотке назад?

Есть, и первое, что сделает seekg - сбросит буфер (возможны реализации stl, где это не так, но это маловероятно)

Цитата

Да, как-то не кузяво.

И не говори smile

Автор: Dims 30.11.2007, 09:51
Цитата(archimed7592 @  29.11.2007,  16:46 Найти цитируемый пост)
то, тогда перебрасывать исключение

А какое? Есть в STL или надо своё определить? Что-то я не вижу в <stdexcept> подходящих.

Цитата(archimed7592 @  29.11.2007,  16:46 Найти цитируемый пост)
Но, боюсь, тебе это не поможет, ибо ни bad_bit, ни исключение обратно данные в поток не засунет... 

Ну это понятно. Я теперь решил сделать проверку символа перед чтением, но всё же надо же что-то делать, если я ошибусь. Для этого и исключение.

Автор: archimed7592 30.11.2007, 10:56
Стандартные бросают ios_base::failure, который в свою очередь, является наследником std::exception.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)