2016-09-26 7 views
3

Я хочу проверить, заканчивается ли поток (на самом деле ifstream) символом новой строки. Я пришел с этим:Проверьте, заканчивается ли поток с помощью новой строки

bool StreamEndsWithNewline(std::basic_istream<char> & the_stream) 
{ 
    if (the_stream.peek() == EOF) { 
     the_stream.clear(); //clear flags set by peek() 
     return false; 
    } 
    std::string line = "blah"; 
    while (std::getline(the_stream, line)) { 
     // ... 
    } 
    return line.empty(); 
} 

Идея заключается в том, что если последняя линии потока имеет \n конечный символ, цикл, пока будет делать еще одну итерации (поскольку ВФ не был достигнут), в котором пустая строка будет назначена аргументу строки.

Специальный случай «пустого» потока должен обрабатываться отдельно.

Кажется, что работает над окнами (vs2010). Могу ли я это сделать в целом?

+0

файлов должны заканчиваться символом новой строки. Этот тест обнаружит поток, который либо ** ** ** только одна новая строка, либо заканчивается ** двумя или более символами новой строки. –

+0

Текстовый поток, который не заканчивается символом новой строки, не является допустимым входным потоком в C или C++. Операции ввода в этом потоке не требуются для разумного обращения. –

+0

Итак, с std :: stringstream the_stream (""); the_stream << "привет!"; является входным сигналом the_stream? Я считаю, что это то, что я получаю, если читаю в файле (используя ifstream), который не заканчивается новой строкой. – Jens

ответ

1

tldr; Да, это гарантированно работает, если поток изначально пуст.


Есть два бита учитывать следующие аспекты: fail бит и eof бит. std::getline делает из [string.io]:

После построения sentry объекта, если караул превращается в истинный, вызывает str.erase(), а затем извлекает символы из это и добавляет их к str как при вызове str.append(1, c) [. ..] Если функция не извлекает никаких символов, он вызывает is.setstate(ios::failbit)

И sentry делает из [IStream :: часового]:

Эффекты: если is.good() - false, звонки is.setstate(failbit). В противном случае готовится форматированный или неформатированный ввод. [...] Если is.rdbuf()->sbumpc() или is.rdbuf()->sgetc() возвращает traits::eof(), функция вызывает setstate(failbit | eofbit)

Поэтому, учитывая все это, давайте разберем два примера:


Случай 1: "hello\n". Первый вызов getline(), the_stream.good() истинен, мы извлекаем символы вверх через \n, поток по-прежнему good(), и мы вводим тело цикла с line, установленным в "hello".

Второй вызов getline(), поток по-прежнему good(), поэтому sentry объект превращается в истинное, и мы называем str.erase(). Попытка извлечь последующие символы не удалась, так как мы закончили с потоком, поэтому установлен failbit. Это приводит к возврату getline() для преобразования в false, поэтому мы не вводим тело цикла во второй раз. В конце цикла line пуст.


Дело 2: "goodbye", no newline. Первый вызов getline(), the_stream.good() правдив, мы извлекаем символы, пока не нажмем eof().Поток failbit еще не установлен, поэтому мы по-прежнему вводим тело цикла, с линией, установленной на "goodbye".

Второй вызов getline(), строительство sentry объекта не удается, потому что is.good() является ложным (is.good() проверяет как eofbit и failbit). Из-за этого отказа мы не переходим на первый шаг getline(), который вызывает str.erase(). И из-за этого отказа, failbit установлен таким образом, чтобы мы снова не вошли в тело цикла.

В конце цикла line по-прежнему "goodbye".


Дело 3: "". Здесь getline() не извлекает никаких символов, поэтому устанавливается failbit, и цикл никогда не вводится, а line всегда пуст. Есть несколько способов, чтобы дифференцировать этот случай от случая 1:

  • Вы можете, фронт, peek(), чтобы увидеть, если первый символ traits::eof(), прежде чем делать что-нибудь еще.
  • Вы можете подсчитать, сколько раз вы вводите цикл и убедитесь, что оно отличное от нуля.
  • Вы можете инициализировать line некоторому неточному значению часового. В конце цикла линия будет пустой, если поток заканчивается делителем.
+0

Повторите пример 'hello \ n', да, я согласен, я получу одну строку, но цикл while сделает дополнительную итерацию, потому что eof не достигнут. И в этой итерации я получаю пустую строку, назначенную параметру строки. Поэтому мне кажется, что мне не нужна явная проверка eof. – Jens

+1

@Jens Нет, нет. Следующий вызов 'getline()' закончится неудачным потоком, поэтому вы не входите в цикл. – Barry

+1

Я согласен с тем, что я не вхожу в цикл, но перед входом в цикл вам будет присвоено новое значение, нет? – Jens

1

Ваш код работает.

Однако, вы можете попробовать поиск потока и протестировать только последний символ или отбрасывать символы следующим образом: потоки

#include <cassert> 
#include <iostream> 
#include <limits> 
#include <sstream> 

bool StreamEndsWithNewline(std::basic_istream<char>& stream) { 
    const auto Unlimited = std::numeric_limits<std::streamsize>::max(); 
    bool result = false; 
    if(stream) { 
     if(std::basic_ios<char>::traits_type::eof() != stream.peek()) { 
      if(stream.seekg(-1, std::ios::end)) { 
       char c; 
       result = (stream.get(c) && c == '\n'); 
       stream.ignore(Unlimited); 
      } 
      else { 
       stream.clear(); 
       while(stream && stream.ignore(Unlimited, '\n')) {} 
       result = (stream.gcount() == 0); 
      } 
     } 
     stream.clear(); 
    } 
    return result; 
} 

int main() { 
    std::cout << "empty\n"; 
    std::istringstream empty; 
    assert(StreamEndsWithNewline(empty) == false); 

    std::cout << "empty_line\n"; 
    std::istringstream empty_line("\n"); 
    assert(StreamEndsWithNewline(empty_line) == true); 

    std::cout << "line\n"; 
    std::istringstream line("Line\n"); 
    assert(StreamEndsWithNewline(line) == true); 

    std::cout << "unterminated_line\n"; 
    std::istringstream unterminated_line("Line"); 
    assert(StreamEndsWithNewline(unterminated_line) == false); 

    std::cout << "Please enter ctrl-D: (ctrl-Z on Windows)"; 
    std::cout.flush(); 
    assert(StreamEndsWithNewline(std::cin) == false); 
    std::cout << '\n'; 

    std::cout << "Please enter Return and ctrl-D (ctrl-Z on Windows): "; 
    std::cout.flush(); 
    assert(StreamEndsWithNewline(std::cin) == true); 
    std::cout << '\n'; 

    return 0; 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^