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

P.S.
Я использую VS2010 sp1


person smallB    schedule 08.06.2011    source источник
comment
Ты серьезно? getline работает неправильно? Невероятный!   -  person    schedule 08.06.2011
comment
@Влад Я просто не уверен, наверное это я что-то делаю, но не вижу где.   -  person smallB    schedule 08.06.2011


Ответы (5)


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

while( getline( cin, line ) ) {
    // do something with line
}
person Community    schedule 08.06.2011
comment
@ Нил, а есть ли какая-то особая причина не использовать eof? - person smallB; 08.06.2011
comment
@Bruce: как насчет того, чтобы погуглить идиому safe bool, прежде чем голосовать против ответа на вопрос о языке, который вы явно недостаточно хорошо понимаете? +1 в качестве компенсации, это правильный ответ. - person rubenvb; 08.06.2011
comment
@Neil, и то, как вы показали, несколько не работает с моим многомерным массивом, я получаю сообщение об ошибке. Ни один экземпляр перегруженной функции не соответствует списку аргументов. - person smallB; 08.06.2011
comment
@smallB Использование eof() обычно является попыткой угадать, что будет делать СЛЕДУЮЩИЙ ввод-вывод, когда на самом деле он говорит вам, что сделал ПРЕДЫДУЩИЙ. Что касается того, что это не работает с вашим кодом, то есть много причин, по которым это может быть не так. Когда вы читаете char[] (еще раз, почему???), вам нужно while(file.getline(...)). - person ; 08.06.2011
comment
@Neil, это не работает, потому что, как я уже сказал, я получаю сообщение об ошибке от IntelliSense, говорящее мне, что эта функция не может принять эти параметры. - person smallB; 08.06.2011
comment
@Neil Я понял, что если у меня есть число из 15 цифр, я должен установить для своего массива значение char arr[][16] и при чтении строки также 16 символы должны быть прочитаны. Но спасибо за ваш ответ. - person smallB; 08.06.2011

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

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

Тесты для !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 '\0'
    for (unsigned lines = 0;
         lines < 4 && file.getline(numbers[lines], 16);
         ++lines)
    {
        std::cout << "numbers[" << lines << "] = " << numbers[lines] << '\n';
    }
}

проверено на Visual Studio 2010 SP1

person Cubbi    schedule 08.06.2011
comment
Нет никаких оснований предполагать, что он столкнется с каким-либо сбоем, так зачем устанавливать failbit? - person rcollyer; 08.06.2011
comment
@rcollyer сбой в данном случае — это невыполнение запрошенной операции (прочитать строку) - person Cubbi; 08.06.2011
comment
Я должен был прочитать немного дальше. Заставляет меня больше ценить std::string версию getline. - person rcollyer; 08.06.2011

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

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

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

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

person Bruce    schedule 08.06.2011

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

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

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

person Gabriel    schedule 08.06.2011

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

#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] = '\0';
        }
    }

}
person Jason    schedule 08.06.2011
comment
из getline doc завершающий нуль должен быть добавлен автоматически, затем вы выполняете одну и ту же работу дважды, сначала копируете из файла во временный файл, а затем копируете то же самое из временного файла в числа. ДОЛЖЕН быть лучший способ! - person smallB; 08.06.2011
comment
-1: Вау, ты так надеешься, что в getline ничего не пойдет не так. - person rubenvb; 08.06.2011
comment
Хорошо, я добавил еще несколько функций безопасности в вызов getline. - person Jason; 08.06.2011
comment
@smallB: я сократил код, но вы определенно захотите добавить функцию безопасности, которая завершает ваши строки в случае getline сбоя. - person Jason; 08.06.2011