Как правильно выполнить проверку ошибок «перехватить все» в операции вывода fstream? - PullRequest
2 голосов
/ 09 апреля 2010

Как правильно проверить общую ошибку при отправке данных в fstream?

ОБНОВЛЕНИЕ : Моя главная проблема связана с тем, что я слышал о задержке между выводом и физическими данными, записываемыми на жесткий диск. Я предполагал, что команда «save_file_obj << save_str» будет отправлять данные только в какой-то буфер и что следующая проверка «if (save_file_obj.bad ())» не будет использоваться для определения наличия ОС или оборудования проблема. Я просто хотел знать, что является окончательным способом «перехватить все», чтобы отправить строку в файл и проверить, чтобы убедиться, что она была записана на диск, прежде чем выполнять какие-либо следующие действия, такие как закрытие программы. </p>

У меня есть следующий код ...

int Saver::output()
{
    save_file_handle.open(file_name.c_str());
    if (save_file_handle.is_open())
    {
        save_file_handle << save_str.c_str();

        if (save_file_handle.bad())
        {
            x_message("Error - failed to save file");
            return 0;
        }

        save_file_handle.close();

        if (save_file_handle.bad())
        {
            x_message("Error - failed to save file");
            return 0;
        }

        return 1;
    }
    else
    {
        x_message("Error - couldn't open save file");
        return 0;
    }
} 

Ответы [ 3 ]

4 голосов
/ 09 апреля 2010

Несколько баллов. Во-первых:

save_file_handle

- плохое имя для экземпляра потока C ++. fstreams не являются файловыми дескрипторами, и все, что можно сделать, это запутать читателя.

Во-вторых, как указывает Майкл, нет необходимости преобразовывать строку C ++ в строку C. Единственный раз, когда вы действительно должны это делать, это когда вы взаимодействуете с APIS в стиле C и используете несколько плохо разработанных API C ++, таких как (к сожалению) fstream :: open ().

В-третьих, канонический способ проверки работоспособности потоковой операции - проверка самой операции. Потоки имеют преобразование в void *, что означает, что вы можете написать что-то вроде этого:

if ( save_file_handle << save_str ) {
   // operation worked
}
else {
   // failed for some reason
}

Ваш код должен всегда проверять операции потока v, будь то для ввода или вывода.

2 голосов
/ 09 апреля 2010

Вы абсолютно уверены, что save_file_handle не имеет файла, связанного (открытого) с ним? Если это произойдет, то вызов метода open() завершится неудачей и поднимет флаг ошибки ios::failbit - и любые исключения, если он будет установлен.

Метод close() не может завершиться ошибкой, если файл не открыт, и в этом случае метод вызовет флаг ошибки ios::failbit. В любом случае деструктор должен закрыть файл и сделать это автоматически, если save_file_handle является переменной стека, как в вашем коде.

int Saver::output()
{
    save_file_handle.open(file_name.c_str());
    if (save_file_handle.fail())
    {
        x_message("Error - file failed to previously close");
        return 0;
    }
    save_file_handle << save_str.c_str();

    if (save_file_handle.bad())
    {
        x_message("Error - failed to save file");
        return 0;
    }    
    return 1;
}

Кроме того, вы можете отделить проверку ошибок от логики сохранения файла, если вы используете ios::exceptions().

int Saver::output()
{
    ios_base::iostate old = save_file_handle.exceptions();
    save_file_handle.exceptions(ios::failbit | ios::badbit);
    try
    {
        save_file_handle.open(file_name.c_str());          
        save_file_handle << save_str.c_str();
    }
    catch (ofstream::failure e)
    {
        x_message("Error - couldn't save file");
        save_file_handle.exceptions(old);
        return 0;
    }
    save_file_handle.exceptions(old);
    return 1;
}

Вы можете предпочесть перевести вызов на save_file_handle.exceptions(ios::failbit | ios::badbit) в конструктор (ы). Затем вы можете избавиться от операторов, которые сбрасывают флаг исключений.

2 голосов
/ 09 апреля 2010

Все, кроме проверки после закрытия, кажется разумным. Тем не менее, я бы перестроил вещи немного по-другому и выбросил бы исключение или использовал бы bool, но это просто вопрос предпочтения:

bool Saver::output()
{
    std::fstream out(_filename.c_str(),std::ios::out);
    if ( ! out.is_open() ){
         LOG4CXX_ERROR(_logger,"Could not open \""<<filename<<"\"");
         return false;
    }

    out << _savestr << std::endl;
    if ( out.bad() ){
         LOG4CXX_ERROR(_logger,"Could not save to \""<<filename<<"\"");
         out.close();
         return false;
    }

    out.close();
    return true;
}

Я также должен указать, что вам не нужно использовать save_str.c_str(), поскольку все потоки C ++ (включая fstream, ofstream и т. Д.) Способны выводить объекты std :: string. Кроме того, если вы создадите объект потока файлов в области действия функции, он автоматически закроется, когда выйдет из области видимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...