Почему размер файла отличается от времени чтения файла размером 1 байт? - PullRequest
0 голосов
/ 09 мая 2020

Я изучаю обработку файлов C, и у меня возникла проблема. Я написал коды следующим образом:

# include <stdio.h>
# include <stdlib.h>

int main(void)
{
    FILE * file;
    errno_t err = fopen_s(&file,"f.txt","r");

    fseek(file, 0, SEEK_END);
    int size = ftell(file);
    fseek(file, 0, SEEK_SET);

    char *tmp;
    tmp = malloc(size);
    printf("%d\n", size);

    for (int i = 0; !feof(file); i++)
    {
        fread(tmp + i, 1, 1, file);
        size = i + 1;
    }

    printf("%d\n", size);

    fclose(file);
    free(tmp);
    return 0;

Однако выходные размеры не совпадают (1-й: 78, 2-й: 76), в чем причина этого?

1 Ответ

2 голосов
/ 09 мая 2020

Я подозреваю, что вы используете Microsoft Windows. В реализации Microsoft C / C ++ двоичные потоки и текстовые потоки отличаются. Если бы вы открыли файл с "rb", переданным в fopen_s в качестве третьего параметра, файл будет открыт с двоичным потоком, и fread вернет фактические байты в файле.

Поскольку вы открыли файл с "r", он был открыт как текстовый поток. В этом режиме при чтении и записи файла выполняется некоторая обработка. Примечательно, что Windows использует два символа: новую строку '\n' и символ возврата каретки '\r' в конце каждой строки. При чтении файла в виде текстового потока эти два символа сокращаются до одного '\n'. И наоборот, при записи текстового потока запись '\n' дает '\n' и '\r' в файле.

Для двоичного потока ftell дает количество байтов от начала файла. . Для текстового потока стандарт C указывает только, что ftell можно использовать для сброса позиции потока с помощью fseek - это не обязательно количество байтов (в фактическом файле) или символов (появляющихся в потоке) с начала файла. Реализация C может реализовывать ftell так, чтобы она давала количество байтов от начала файла (а это 78, которые вы видите), но, даже если это так, вы не можете легко использовать это, чтобы узнать, как в текстовом потоке много символов.

Кроме того, как другие отмечали в комментариях, этот код неверен:

for (int i = 0; !feof(file); i++)
    {
        fread(tmp + i, 1, 1, file);
        size = i + 1;
    }

Стандартные библиотечные процедуры не знают конец файла файла был достигнут до тех пор, пока вы не попытаетесь прочитать, и он не удастся, поскольку был достигнут конец файла. Например, если в файле есть один символ, и вы его читаете, feof(file) все равно ложно - конец файла не обнаружен. Только когда вы попытаетесь прочитать второй символ, а fread не удастся, feof(file) станет истинным.

Из-за этого приведенное выше l oop в конечном итоге устанавливает size на единицу больше, чем количество прочитанных символов, потому что в начале итерации файла !feof(file) было истинным, поэтому была предпринята попытка fread, она не удалась, а затем size было установлено на i + 1, даже если не было байта был только что прочитан.

Так как feof работает, вы не можете использовать его для управления al oop таким образом. Вместо этого вы должны написать l oop, чтобы он проверял результат fread, и, если он не может прочитать какие-либо символы, код выходит из l oop. Этот код может быть примерно таким:

int i = 0;
do
{
    size_t result = fread(tmp + i, 1, 1, file);
    if (result == 0)
        break;
    i++;
}
size = i;

(Обратите внимание, что если вы читали более одного байта с помощью fread, потребуется дополнительный код для обработки случая, когда количество байтов чтение было между нулем и запрошенным числом.)

После того, как l oop будет исправлено, вы должны увидеть количество символов в потоке, указанное как 75. Скорее всего, ваш файл f.txt содержит три строки текста длиной 72 символа, исключая окончания строк. При чтении в виде текстового потока имеется три '\n' символов, итого 75. При чтении в виде двоичного потока есть три '\n' символа и три '\r' символа, итого 78.

...