Вопросы по поводу проверки
От: YourLastSong  
Дата: 14.06.11 16:23
Оценка:
Здравствуйте, уважаемые господа.

— Программа должна считывать из текстового файла данные, которые вводил пользователь.

Файл может содержать любое кол-во строк, в каждой из которых должны находиться данные в формате "час минуты событие", т.е. 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 () вообще?

Заранее благодарю за возможные ответы.
Re: Вопросы по поводу проверки
От: zaufi Земля  
Дата: 14.06.11 17:28
Оценка:
Здравствуйте, 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

как-то так вкраце...
Re: Вопросы по поводу проверки
От: morm Россия  
Дата: 14.06.11 18:07
Оценка:
Здравствуйте, YourLastSong, Вы писали:

YLS>Здравствуйте, уважаемые господа.


YLS>- Программа должна считывать из текстового файла данные, которые вводил пользователь.




#include <string>
#include <iterator>
#include <fstream>
#include <exception>
#include <algorithm>
#include <vector>

struct user_data
{
    int hour;
    int min;
    std::string event_msg;
};

std::istream& operator >> (std::istream& stream, user_data& data)
{
    stream >> data.hour;
    if(stream.fail() || (data.hour < 0 || data.hour >=24))
        throw std::exception("Не верный формат времени. Час не соответствует формату файла");
    stream >> data.min;
    if(stream.fail() || (data.min < 0 || data.min >=60))
        throw std::exception("Не верный формат времени. Минуты не соответствуют формату файла");
    std::string tmp;
    while(!stream.eof() && stream.peek() != '\n')
    {
        stream >> tmp;
        data.event_msg += tmp + " ";
    }
    //проверка строки на возможные значения

    return stream;
}


int main(int argc, char* argv[])
{    
    std::vector<user_data> data_vec;
    
    try
    {
        std::fstream fi = std::fstream("d:\\log.txt");
        //ну проверочку сам сваяешь
    
        std::copy(std::istream_iterator<user_data>(fi), std::istream_iterator<user_data>(), std::back_inserter(data_vec));
    }
    catch(std::exception&)
    {
    }
    return 0;
}


На скорую руку — что то типа того. Не самый быстрый код, зато структурно понятно и вроде то, что тебе надо
Re: Вопросы по поводу проверки
От: Centaur Россия  
Дата: 14.06.11 18:22
Оценка:
Здравствуйте, YourLastSong, Вы писали:

YLS>- Программа должна считывать из текстового файла данные, которые вводил пользователь.


YLS>Файл может содержать любое кол-во строк, в каждой из которых должны находиться данные в формате "час минуты событие", т.е. int int string.


YLS>Проблема в том, что проверки на то, что именно добавил в файл пользователь нет вообще, поэтому пользователь вместо int может написать, например, string и т.д.


YLS>Как реализовать данную проверку, если для этого я использую ifstream?


В случае неудачи чтения у потока выставится флаг badbit. Если у потока включены исключения по badbit’у, то ещё и вылетит исключение.

YLS>Пока что придумал только вот такой способ при помощи инициализации переменных:


YLS>int timeh, time_min;
YLS>std::string str;
YLS>    while (!f.eof ())
YLS>    {
YLS>        timeh = 30;
YLS>        time_min = 80;

Это не инициализация. Это присваивание.

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

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 () вообще?


27.5.4.3 basic_ios flags functions [iostate.flags]

[…]
bool operator!() const;

2 Returns: fail().

[…]

bool fail() const;

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>Заранее благодарю за возможные ответы.


Благодарят, как правило, после, а не заранее. (Кстати, весь текст по ссылке рекомендуется к прочтению.) Тут даже есть кнопки для «спасибо».
Re: Вопросы по поводу проверки
От: Centaur Россия  
Дата: 15.06.11 02:48
Оценка:
Здравствуйте, 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.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.