fstream дает мне неправильный размер файла

Я написал простую функцию, которая считывает весь файл в буфер.

#include <iostream>
#include <fstream>
int main()
{
    std::ios_base::sync_with_stdio(0);
    std::ifstream t;
    t.open("C:\\Users\\sufal\\Desktop\\test.txt");
    t.seekg(0, std::ios::end);    
    long length = t.tellg();           
    t.seekg(0, std::ios::beg);  
    std::cout << "file size: " << length << std::endl;
    char* buffer = new char[length+1];    
    t.read(buffer, length);       
    t.close();
    buffer[length] = 0;
    std::cout << buffer << std::endl;

    
    return 0; 
}

А это test.txt:

1
2
3

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

Размер файла должен быть 5 байтов. Почему моя программа показывает неправильный размер файла? Проводник Windows также показывает неправильный размер файла - 7 байт.


person olaf    schedule 29.11.2020    source источник
comment
Это не решает вопрос l, а дает привычку инициализировать объекты значимыми значениями, а не инициализировать их по умолчанию и немедленно перезаписывать значения по умолчанию. В данном случае это означает изменение std::ifstream t; t.open("C:\\Users\\sufal\\Desktop\\test.txt”); на std::ifstream t("C:\\Users\\sufal\\Desktop\\test.txt");. Кроме того, вам не нужно звонить t.close();. Это сделает деструктор.   -  person Pete Becker    schedule 30.11.2020


Ответы (3)


В Windows символ новой строки "\r\n", состоящий из двух байтов. Итак, если ваш файл не заканчивается новой строкой, 7 действительно его размер:

1     <-- 1 byte for '1', 2 bytes for CRLF
2     <-- 1 byte for '2', 2 bytes for CRLF
3     <-- 1 byte for '3'

Чтобы правильно прочитать файл на байтовом уровне, вам нужно открыть его в двоичном режиме:

t.open("C:\\Users\\sufal\\Desktop\\test.txt", ios_base::binary);

(вы можете прочитать о деталях этого поведения в документации).

Вы также можете увидеть другие варианты чтения всего файла в строку в C ++:

person BartoszKP    schedule 29.11.2020
comment
Значит, двоичный режим также применим для чтения текстовых файлов? - person olaf; 30.11.2020
comment
@olaf Нет, но ваш код написан таким образом, что предполагается чтение двоичного файла - побайтно. Без этого флага ifstream интерпретирует символы новой строки и изменяет их, следовательно, ваши артефакты. См. Связанные вопросы и ответы на них, чтобы узнать о том, как прочитать файл, используя то, что он является текстовым файлом. - person BartoszKP; 30.11.2020

Ваш файл имеет размер 7 байт, потому что он использует разрывы строк CRLF.

1[cr][lf]
2[cr][lf]
3

Но вы открываете файл в текстовом режиме, который в Windows нормализует разрывы строк CRLF на LF. Вы выделяете 7 char для своего буфера, но read() выводит только 5 char:

1[lf]
2[lf]
3

Вот почему вы видите лишние 2 = в конце вывода на печать, потому что вы не обнулили неиспользуемое буферное пространство, поэтому вы видите случайный мусор из неинициализированной памяти.

Вместо этого откройте файл в двоичном режиме.

t.open("C:\\Users\\sufal\\Desktop\\test.txt", std::ios_base::binary);

Дополнительные сведения см. В двоичном и текстовом режимах на cppreference.com. .

person Remy Lebeau    schedule 29.11.2020

В Windows этот файл действительно имеет размер 7 байт: 1 \r\n 2 \r\n 3

Windows кодирует новую строку двумя байтами - CR + LF (или \r + \n в другой записи).

Все правильно.

person loa_in_    schedule 29.11.2020
comment
Итак, если я просто хочу прочитать весь файл, как я должен обрабатывать эти двойные символы EOL - person olaf; 30.11.2020
comment
Вы отлично прочтете файл. Вы можете легко предположить, что все символы \r заканчивают строку и пропускают следующий байт. Он ВСЕГДА \r\n в Windows и \r больше нигде не используется (в основном). - person loa_in_; 30.11.2020
comment
\r = 13 в десятичном формате, \n = 10 в десятичном формате - person loa_in_; 30.11.2020
comment
Так в чем же причина появления двух знаков равенства в конце вывода? - person olaf; 30.11.2020
comment
Я не парень C ++, извините. На мой вкус + 1 в length + 1 не нужен, но я беру его по опыту на другом языке. - person loa_in_; 30.11.2020
comment
@loa_in_ + 1 необходимо для хранения 0 в конце буфера, чтобы иметь правильную строку с завершающим нулем, чтобы cout правильно ее напечатал. - person BartoszKP; 30.11.2020
comment
@BartoszKP только потому, что buffer печатается как строка с завершающим нулем. Можно напечатать buffer как есть без использования нулевого терминатора, используя cout.write(buffer, length) вместо cout << buffer - person Remy Lebeau; 30.11.2020
comment
@RemyLebeau Да, я знаю. Я не утверждал, что это единственная возможность - просто сказал, что это имеет смысл. - person BartoszKP; 30.11.2020