похоже, что getline работает некорректно

#c #fstream #getline

#c #fstream #getline

Вопрос:

Пожалуйста, скажите мне, что я здесь делаю не так. Что я хочу сделать, так это это:
1. Наличие текстового файла с четырьмя числами, и каждое из этих чисел состоит из 15 цифр:

 std::ifstream file("numbers.txt",std::ios::binary);
  

Я пытаюсь прочитать эти числа в свой массив:

   char num[4][15];
  

И я думаю, что я делаю следующее: до тех пор, пока вы не дойдете до конца файлов, записывайте каждую строку (максимум 15 символов, заканчивающихся на ‘n’) в num [lines]. Но это несколько не работает. Во-первых, он правильно считывает только первое число, остальное — это просто «» (пустая строка), а во-вторых, file.eof(), похоже, тоже работает некорректно. В текстовом файле, который я представляю ниже этого кода, я достиг строк, равных 156. Что происходит?

                 for (unsigned lines = 0; !file.eof();   lines)
                {
                    file.getline(num[lines],15,'n');  
                }
  

Итак, вся «процедура» выглядит следующим образом:

 int main()
{
std::ifstream file("numbers.txt",std::ios::binary);

char numbers[4][15];

            for (unsigned lines = 0; !file.eof();   lines)
            {
                file.getline(numbers[lines],15,'n');// sizeof(numbers[0])
            }
}
  

Это содержимое моего текстового файла:

111111111111111
222222222222222
333333333333333
444444444444444

PS
Я использую VS2010 sp1

Комментарии:

1. Вы серьезно? getline работает некорректно? Невероятно!

2. @Vlad Я просто не уверен, возможно, это я что-то делаю, но я не вижу, где.

Ответ №1:

Не используйте функцию eof()! Канонический способ чтения строк:

 while( getline( cin, line ) ) {
    // do something with line
}
  

Комментарии:

1. @Neil, а есть ли какая-то конкретная причина не использовать eof?

2. @Bruce: как насчет поиска в Google «безопасной идиомы bool», прежде чем отклонять ответ на вопрос о языке, который вы явно недостаточно хорошо понимаете? 1 для компенсации, это место в ответе.

3. @Neil и способ, который вы показали, несколько не работает с моим многомерным массивом, я получаю сообщение об ошибке «Ни один экземпляр перегруженной функции не соответствует списку аргументов»

4. @smallB Использование eof() обычно является попыткой угадать, что будет делать СЛЕДУЮЩИЙ ввод-вывод, когда на самом деле он сообщает вам, что делал ПРЕДЫДУЩИЙ. Что касается того, что это не работает с вашим кодом, ну, есть много причин, почему это может быть не так. Когда вы читаете char[] (еще раз, почему ???) вы хотите while( file.getline( … ) ).

5. @Neil это не работает, потому что, как я уже сказал, я получаю сообщение об ошибке от IntelliSense, сообщающее мне, что эта функция не может принять эти параметры.

Ответ №2:

file.getline() извлекает 14 символов, заполняя num[0][0] .. num[0][13] . Затем он сохраняет '' в num[0][14] и устанавливает failbit on file , потому что это то, что он делает, когда буфер заполнен, но завершающий символ не достигнут.

Дальнейшие попытки вызвать file.getline() ничего не дают, потому что установлен failbit.

Тесты для !file.eof() возвращают true, потому что eofbit не установлен.

Редактировать: чтобы привести рабочий пример, лучше всего, конечно, использовать строки, но для заполнения вашего массива символов вы могли бы сделать это:

 #include <iostream>
#include <fstream>
int main()
{
    std::ifstream file("numbers.txt"); // not binary!
    char numbers[4][16]={}; // 16 to fit 15 chars and the ''
    for (unsigned lines = 0;
         lines < 4 amp;amp; file.getline(numbers[lines], 16);
           lines)
    {
        std::cout << "numbers[" << lines << "] = " << numbers[lines] << 'n';
    }
}
  

протестировано в Visual Studio 2010 с пакетом обновления 1

Комментарии:

1. Ничто не указывает на то, что он столкнется с каким-либо сбоем, так зачем же failbit устанавливать?

2. @rcollyer сбой в этом случае заключается в неспособности выполнить запрошенную операцию («прочитать строку»)

3. Мне следовало прочитать немного дальше. Заставляет меня больше ценить std::string версию getline .

Ответ №3:

Согласно документу ifstream, чтение прекращается либо после считывания n-1 символов, либо после нахождения знака ограничения: первое чтение заняло бы всего 14 байт.

Он считывает байты: ‘1’ (символ) равен 0x41: ваш буфер будет заполнен 0x41 вместо 1, как вы, кажется, ожидаете, последним символом будет 0 (конец c-строки)

Примечание стороны, ваш код не проверяет, что строки не выходят за пределы вашего массива.

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

Ответ №4:

Похоже, что ‘ n’ в конце первого like не рассматривается и остается в буфере. Итак, в следующем getline() он будет прочитан.

Попробуйте добавить file.get() после каждой getline().

Если один file.get() не работает, попробуйте два, потому что в кодировке файла Windows по умолчанию строка заканчивается на ‘n r ‘ (или ‘ r n’, я никогда не знаю 🙂

Ответ №5:

Измените его на следующее:

 #include <cstring>

int main()
{
    //no need to use std::ios_base::binary since it's ASCII data
    std::ifstream file("numbers.txt");

    //allocate one more position in array for the NULL terminator
    char numbers[4][16];

    //you only have 4 lines, so don't use EOF since that will cause an extra read
    //which will then cause and extra loop, causing undefined behavior
    for (unsigned lines = 0; lines < 4;   lines)
    {
        //copy into your buffer that also includes space for a terminating null
        //placing in if-statement checks for the failbit of ifstream
        if (!file.getline(numbers[lines], 16,'n'))
        {
            //make sure to place a terminating NULL in empty string
            //since the read failed
            numbers[lines][0] = '';
        }
    }

}
  

Комментарии:

1. из getline doc завершающий null должен быть добавлен автоматически, затем вы выполняете примерно дважды одну и ту же работу, сначала вы копируете из file во temp, а затем вы копируете это же из temp в numbers. Должен быть способ получше!

2. -1: Вау, вы очень доверяете тому, что ничего не происходит неправильно в getline .

3. Хорошо, я добавил еще несколько функций безопасности в getline вызов

4. @smallB: Я сократил код, но вы определенно захотите добавить функцию безопасности, которая завершает работу ваших строк в случае getline сбоя.