— Программа должна считывать из текстового файла данные, которые вводил пользователь.
Файл может содержать любое кол-во строк, в каждой из которых должны находиться данные в формате "час минуты событие", т.е. int int string.
Проблема в том, что проверки на то, что именно добавил в файл пользователь нет вообще, поэтому пользователь вместо int может написать, например, string и т.д.
Как реализовать данную проверку, если для этого я использую ifstream?
Пока что придумал только вот такой способ при помощи инициализации переменных:
int timeh, time_min;
std::string str;
while (!f.eof ())
{
timeh = 30;
time_min = 80;
str = "end";
f >> timeh >> time_min >> str;
if (timeh == 30 || time_min == 80 || str == "end")
{
MessageBox (0, "Проверьте, пожалуйста, данные в файле", "A test program", MB_OK | MB_ICONASTERISK);
ExitProcess (0);
}
a1.push_back (str);
h_vec.push_back (timeh);
min_vec.push_back (time_min);
}
— Где будет логичнее инициализировать переменные в данном случае?
Внутри цикла while (!f.eof ()) или нет?
— Проверку на успешное открытие файла лучше всего выполнять при помощи !f, f.bad () или f.fail ()?
Есть ли разница между !f, f.bad () или f.fail () вообще?
Здравствуйте, YourLastSong, Вы писали:
YLS>Здравствуйте, уважаемые господа.
YLS>- Программа должна считывать из текстового файла данные, которые вводил пользователь.
сколько их там? сотни? тысячи? миллионы строк?
в зависимости от этого могут быть разные требования по скорости работы, и, соответственно алгоритмы...
забегая вперед, могу сказать, что выбраный тобой способ далек как от "правильного", и еще дальше от оптимального... )
YLS>Файл может содержать любое кол-во строк, в каждой из которых должны находиться данные в формате "час минуты событие", т.е. int int string.
может тогда лучше говорить про парсинг строк? ибо считывание с помощью операторов форматированного ввода это довольно тормознутая операция
YLS>Как реализовать данную проверку, если для этого я использую ifstream?
YLS>Пока что придумал только вот такой способ при помощи инициализации переменных:
поправлю твой код стараясь не сильно менять логику (при обнаружении всего одной кривой строки заканчивать прогу):
// Turn ON exceptions
f.exceptions(std::ios::failbit | std::ios::badbit | std::ios::eofbit);
try {
YLS> int timeh;
YLS> int time_min;
YLS> std::string str;
bool is_read_in_progress;
for (is_read_in_progress = false; f; is_read_in_progress = false)
YLS> {
YLS> f >> timeh;
is_read_in_progress = true;
f >> time_min;
getline(f, str, '\n'); // read the rest of a line
YLS> a1.push_back (str);
YLS> h_vec.push_back (timeh);
YLS> min_vec.push_back (time_min);
YLS> }
}
catch (const std::ios_base::failure& e) {
if (!f.eof() || is_read_in_progress) {
/// \todo It would be nice to give some hint to user about line number at least... :)
YLS> MessageBox (0, "Проверьте, пожалуйста, данные в файле", "A test program", MB_OK | MB_ICONASTERISK);
exit(EXIT_FAILURE); // from <cstdlib>
}
}
это наиболее простой рефакторинг твоего кода и разумеется не единственный.
BTW, operator<< для строк считывает слово -- поэтому заменено на чтение строки до конца (man getline).
что касается улучшений кода:
* зачем завершать прогу при чтении одной неправильной строки? -- лучше обработать все и вывалить список всех кривых строк -- пусть юзер идет и исправляет...
* если записей дофига, то можно подумать над тем чтоб избавиться от исключений. исключения можно оставить только если это действительно исключительная ситуация и мы не хотим дальше продолжать работать... (или нам просто нечего делать по этому поводу)
* если файл не оч большой, можно просто зачитать все (использовать smart ptr на массив char'ов) или отмапить его в память, натравить boost::split, свалить результат в std::vector<boost::iterator_range<char>> -- чтобы не заниматься тупым копированием строк туда-суда-обратно.
* числа можно либо преобразовывать сразу в контейнер intов, либо использовать boost::transform_view чтобы отложить процесс преобразования до момента пока он действительно не понадбится.
* вместо использования кучи векторов лучше завести отдельную структурку с 3мя полянками и иметь 1 контейнер с ними. впрочем можно заюзать boost::tuple или boost::fusion::vector
Здравствуйте, YourLastSong, Вы писали:
YLS>Здравствуйте, уважаемые господа.
YLS>- Программа должна считывать из текстового файла данные, которые вводил пользователь.
Здравствуйте, YourLastSong, Вы писали:
YLS>- Программа должна считывать из текстового файла данные, которые вводил пользователь.
YLS>Файл может содержать любое кол-во строк, в каждой из которых должны находиться данные в формате "час минуты событие", т.е. int int string.
YLS>Проблема в том, что проверки на то, что именно добавил в файл пользователь нет вообще, поэтому пользователь вместо int может написать, например, string и т.д.
YLS>Как реализовать данную проверку, если для этого я использую ifstream?
В случае неудачи чтения у потока выставится флаг badbit. Если у потока включены исключения по badbit’у, то ещё и вылетит исключение.
YLS>Пока что придумал только вот такой способ при помощи инициализации переменных:
А кроме того, признак конца файла появляется у потока не сразу при достижении конца, а только после попытки что-то считать за концом потока. Так что цикл завершится не по основному условию, а по выходу из середины.
YLS>- Где будет логичнее инициализировать переменные в данном случае?
YLS>Внутри цикла while (!f.eof ()) или нет?
Внутри цикла. Поскольку переменные не должны сохранять своё значение между итерациями цикла, логично сделать их локальными в теле цикла:
while (true)
{
int hours = 30, minutes = 80;
std::string s;
YLS>- Проверку на успешное открытие файла лучше всего выполнять при помощи !f, f.bad () или f.fail ()?
Обычно пишут !f.
YLS>Есть ли разница между !f, f.bad () или f.fail () вообще?
9 Returns: true if failbit or badbit is set in rdstate().[307]
307) Checking badbit also for fail() is historical practice.
bool bad() const;
10 Returns: true if badbit is set in rdstate().
И, действительно, лучше завести структуру с полями для времени и строки, определить для неё операцию чтения из потока и дальше читать весь файл при помощи одного обращения к алгоритму std::copy.
YLS>Заранее благодарю за возможные ответы.
Благодарят, как правило, после, а не заранее. (Кстати, весь текст по ссылке рекомендуется к прочтению.) Тут даже есть кнопки для «спасибо».
Здравствуйте, YourLastSong, Вы писали:
YLS>- Программа должна считывать из текстового файла данные, которые вводил пользователь.
YLS>Файл может содержать любое кол-во строк, в каждой из которых должны находиться данные в формате "час минуты событие", т.е. int int string.
А, да, и ещё. Если файл описывает нечто, происходившее в конкретный день, дата должна тоже указываться. Причём дата/время должны записываться в формате ISO 8601 с указанием часового пояса (YYYY-MM-DD'T'HH:MM[:SS[.ZZZZ]]{+|-}HHMM, например, 2011-06-15T09:43+0700) или в UTC (YYYY-MM-DD'T'HH:MM[:SS[.ZZZZ]]'Z', например, 2011-06-15T02:43Z).
Если же файл описывает периодические события, можно воспользоваться либо форматом ISO 8601 для периодических событий, либо форматом cron.