getline setting failbit вместе с eof - PullRequest
0 голосов
/ 03 июня 2018

Мне известно о происхождении этого поведения, так как оно было очень хорошо объяснено в нескольких постах здесь, в SO, некоторые примечательные примеры:

Почему iostream :: eof внутри условия цикласчитается неправильным?

Использовать getline () без установки failbit

std :: getline бросать при достижении eof

C ++ istream EOF не гарантирует failbit?

И он также включен в std::getline стандарт :

3) Если по какой-либо причине не было извлечено ни одного символа (даже не удаленного разделителя), getline устанавливает failbit и возвращает.

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

MWE:

#include <iostream>
#include <string>
#include <fstream>
#include <sstream>


void f(const std::string & file_name, char comment) {

std::ifstream file(file_name);
file.exceptions(file.failbit);
    try {
          std::string line;

          while (std::getline(file, line).good()) {
          // empty getline sets failbit throwing an exception
            if ((line[0] != comment) && (line.size() != 0)) {
                std::stringstream ss(line);
                // do stuff
            }
        }
    }

    catch (const std::ios_base::failure& e) {
        std::cerr << "Caught an ios_base::failure.\n"
        << "Explanatory string: " << e.what() << '\n'
        << "Error code: " << e.code() << '\n';

        }
}


int main() {

    f("example.txt", '#');
}

, где example.txt - файл с разделителями табуляции, последняя строка которого - только\n char:

# This is a text file meant for testing
0   9
1   8
2   7

EDIT:

while(std::getline(file, line).good()){...} повторяет проблему.

Ответы [ 2 ]

0 голосов
/ 03 июня 2018

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

    std::ifstream file (file_name);
    file.exceptions (file.failbit);
    try {
        std::string line;

        while (std::getline(file, line)) {
            // detect empty line and return
            if (line.size() == 0)
                return;
            if (line[0] != comment) {
                std::stringstream ss(line);
                // do stuff
            }
        }
    }
    ...

Другой вариант - проверить, установлено ли eofbit в catch.Если установлено eofbit - чтение успешно завершено.Например,

    catch (const std::ios_base::failure& e) {
        if (!file.eof())
            std::cerr << "Caught an ios_base::failure.\n"
            << "Explanatory string: " << e.what() << '\n'
            << "Error code: " /* << e.code() */ << '\n';
    }
0 голосов
/ 03 июня 2018

Редактировать: Я неправильно понял ОП, см. Ответ Дэвида выше.Этот ответ предназначен для проверки наличия в файле завершающего символа новой строки.

В конце цикла while (getline) проверьте наличие file.eof().

Предположим, вы только что сделалиstd::getline() для последней строки в файле.

  • Если после него стоит \n, то std::getline() прочитал разделитель и не установил eofbit.(В этом случае следующий std::getline() установит eofbit.)

  • Принимая во внимание, что если после него нет \n, то std::getline() прочитал EOF и сделалset eofbit.

В обоих случаях следующий std::getline() вызовет failbit и введет ваш обработчик исключений.

PS: строка if ((line[0] != comment) && (line.size() != 0)) { является UB, если line пусто.Порядок условий необходимо изменить.

...