У меня есть код со следующим фрагментом:
std::string input;
while(std::getline(std::cin, input))
{
//some read only processing with input
}
Когда я запускаю код программы, я перенаправляю ввод stdin через файл in.txt (который был создан с помощью gedit), и он содержит:
ABCD
DEFG
HIJK
Каждая из приведенных выше строк заканчивается одной новой строкой в файле in.txt.
Проблема, с которой я сталкиваюсь, заключается в том, что после того, как цикл while запускается 3 раза (для каждой строки), управление программой не движется вперед и застревает. Мой вопрос: почему это происходит и что я могу сделать, чтобы решить проблему?
Я хочу, чтобы иметь возможность запустить программу из командной строки как так:
$ gcc program.cc -o out
$ ./out < in.txt
Я сделал некоторую отладку и обнаружил, что цикл while фактически выполняется 4 раза (четвертый раз с вводом в виде пустой строки). Это заставляет цикл запрограммировать остановку, потому что // некоторая обработка только для чтения с вводом не может делать свою работу.
Итак, мой уточненный вопрос:
1) Почему работает 4-й цикл?
Обоснование наличия std :: getline () в условии цикла while
должно быть, когда getline () больше не может читать ввод, он возвращает
ноль и, следовательно, цикл while обрывается.Вопреки этому, пока цикл
вместо этого продолжается с пустой строкой! Почему тогда есть getline в
в то время как условие цикла вообще? Разве это не плохой дизайн?
2) Как я могу убедиться, что while не запускается в четвертый раз без использования операторов break?
На данный момент я использовал оператор break и поток строк следующим образом:
std::string input; char temp; while(std::getline(std::cin, input)) { std::istringstream iss(input); if (!(iss >>temp)) { break; } //some read only processing with input }
Но явно должен быть более элегантный способ.
Вопреки Ответ DeadMG, Я полагаю, что проблема связана с содержимым вашего входного файла, а не с вашими ожиданиями относительно поведения символа новой строки.
ОБНОВИТЬ : Теперь, когда у меня была возможность поиграть с gedit
Я думаю, я вижу, что вызвало проблему. gedit
по-видимому, он создан для того, чтобы затруднить создание файла без новой строки в последней строке (что является разумным поведением). Если вы откроете gedit
и введите три строки ввода, набрав Войти в конце каждой строки, затем сохраните файл, он на самом деле создаст 4-строчный файл, а 4-я строка будет пустой. Полное содержание файла, используя ваш пример, будет "ABCD\nEFGH\nIJKL\n\n"
, Чтобы не создавать лишнюю пустую строку, просто не вводите Войти в конце последней строки; gedit
предоставит вам необходимый символ новой строки для вас.
(В особом случае, если вы вообще ничего не вводите, gedit
создаст пустой файл.)
Обратите внимание на это важное различие: в gedit
набрав Войти создает новую строку. В текстовом файле, хранящемся на диске, символ новой строки (LF, '\n'
) обозначает конец текущей строки.
Представления текстовых файлов варьируются от системы к системе. Наиболее распространенные представления для маркера конца строки — это одиночный символ ASCII LF (новая строка) (Unix, Linux и аналогичные системы) и последовательность из двух символов CR и LF (MS Windows). Я возьму Unix-подобное представление здесь. (ОБНОВЛЕНИЕ: В комментарии вы сказали, что используете Ubuntu 12.04 и gcc 4.6.3, поэтому текстовые файлы обязательно должны быть в формате Unix.)
Я только что написал следующую программу на основе кода в вашем вопросе:
#include <iostream>
#include <string>
int main() {
std::string input;
int line_number = 0;
while(std::getline(std::cin, input))
{
line_number ++;
std::cout << "line " << line_number
<< ", input = \"" << input << "\"\n";
}
}
и я создал 3-строчный текстовый файл in.txt
:
ABCD
EFGH
IJHL
В файле in.txt
каждая строка заканчивается одним символом новой строки.
Вот вывод, который я получаю:
$ cat in.txt
ABCD
EFGH
IJHL
$ g++ c.cpp -o c
$ ./c < in.txt
line 1, input = "ABCD"line 2, input = "EFGH"line 3, input = "IJHL"$
Последний символ новой строки в самом конце файла не начинается с новой строки, он просто отмечает конец текущей строки. (Текстовый файл, который не заканчивается символом новой строки, может быть даже недействительным, в зависимости от системы.)
Я могу получить описанное вами поведение, если добавлю второй символ новой строки до конца in.txt
:
$ echo '' >> in.txt
$ cat in.txt
ABCD
EFGH
IJHL
$ ./c < in.txt
line 1, input = "ABCD"line 2, input = "EFGH"line 3, input = "IJHL"line 4, input = ""$
Программа видит пустую строку в конце входного файла потому что есть пустая строка в конце входного файла.
Если вы изучите содержимое in.txt
, ты найдешь два символы новой строки (LF) в самом конце, один для обозначения конца третьей строки и один для обозначения конца (пустой) четвертой строки. (Или, если это текстовый файл в формате Windows, вы найдете последовательность CR-LF-CR-LF в самом конце файла.)
Если ваш код неправильно обрабатывает пустые строки, вы должны либо убедиться, что он не получает пустых строк на входе, либо, что лучше, изменить его так, чтобы он правильно обрабатывал пустые строки. Как он должен обрабатывать пустые строки? Это зависит от того, что программа должна делать, и, вероятно, это полностью зависит от вас. Вы можете молча пропускать пустые строки:
if (input != "") {
// process line
}
или вы можете рассматривать пустую строку как ошибку:
if (input == "") {
// error handling code
}
или вы можете рассматривать пустые строки как действительные данные.
В любом случае вы должны решить, как именно вы хотите обрабатывать пустые строки.
Почему работает 4-й цикл?
Потому что текстовый ввод содержит четыре строки.
Символ новой строки означает только это — «Начать новую строку». Это не означает «предыдущая строка завершена», и в этом тесте выявляется разница между этими двумя семантиками. Итак, мы имеем
1. ABCD
2. DEFG
3. HIJK
4.
Символ новой строки в конце третьей строки начинает новую строку — так же, как он должен делать, и так же, как его имя говорит, что будет. Тот факт, что эта строка пуста, является причиной возврата пустой строки. Если вы хотите избежать этого, обрежьте новую строку в конце третьей строки или просто в специальном случае. if (input == "") break;
,
Проблема не имеет никакого отношения к вашему коду и заключается в том, что вы ошибочно ожидаете поведения символа новой строки.
Изменить: Пожалуйста, прочитайте принятый ответ для правильного объяснения проблемы и решения, а также.
В качестве примечания для людей, использующих std :: getline () в их состоянии цикла while, не забудьте проверить, является ли это пустая строка внутри цикла, и разбить соответствующим образом, например так:
string input;
while(std::getline(std::cin, input))
{
if(input = "")
break;
//some read only processing with input
}
Мое предложение: вообще не включать std :: getline () в цикл while. Скорее используйте std :: cin вот так:
while(std::cin>>a>>b)
{
//loop body
}
Таким образом, дополнительная проверка на пустую строку не потребуется, и дизайн кода лучше.
Последний метод, упомянутый выше, отменяет явную проверку пустой строки (однако, всегда лучше сделать как можно больше явной проверки формата ввода).