【问题标题】:Why is the file size different with the times of file read of 1 bytes?为什么文件大小与文件读取1字节的次数不同?
【发布时间】:2020-05-09 10:09:26
【问题描述】:

我正在学习 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;

但是size的输出不一样(1st: 78, 2nd: 76),这是什么原因呢?

【问题讨论】:

  • 尝试以二进制模式打开文件(否则,在 Windows 中,库会将 "\r\n" 转换为 "\n"
  • for (int i = 0; !feof(file); i++) -> Why is “while ( !feof (file) )” always wrong?
  • 为什么 feof 错误如此普遍? :(
  • @M.M:为什么void main() 错误如此普遍?我可以看到人们学习 feof 并决定以错误的方式使用它,但 main 只是死记硬背。谁教void main()
  • @EricPostpischil 是的,我也在问我同样的问题。看到它得到如此广泛的使用有点烦人也令人沮丧,尽管它是无意义的,每个现代编译器都应该给出一个诊断。

标签: c file io


【解决方案1】:

我怀疑您使用的是 Microsoft Windows。在微软的 C/C++ 实现中,二进制流和文本流是不同的。如果您打开文件时将"rb" 作为其第三个参数传递给fopen_s,则文件将以二进制流打开,fread 将返回文件中的实际字节数。

由于您使用"r" 打开文件,它以文本流的形式打开。在这种模式下,读写文件时会进行一些处理。值得注意的是,Windows 在每行的末尾使用两个字符,一个换行符'\n' 和一个回车符'\r'。将文件作为文本流读取时,这两个字符会缩减为单个 '\n'。相反,在写入文本流时,写入'\n' 会在文件中生成'\n''\r'

对于二进制流,ftell 给出从文件开头开始的字节数。对于文本流,C 标准仅指定 ftell 可用于使用 fseek 重置流位置 - 它不一定是字节数(在实际文件中)或字符数(出现在流中)文件的开头。 C 实现可能会实现ftell,以便它给出从文件开头开始的字节数(即您所看到的 78),但是,即使这样做,您也不能轻易使用它来知道有多少个字符在文本流中。

另外,正如其他人在 cmets 中指出的那样,此代码是错误的:

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

在您尝试读取之前,标准库例程不知道文件已到达文件末尾,但由于已到达文件末尾而失败。例如,如果文件中有一个字符,而您读取了它,feof(file) 仍然为 false — 尚未遇到文件末尾。直到您尝试读取第二个字符并且fread 失败,feof(file) 才会变为 true。

因此,上述循环最终将size 设置为比读取的字符数多一,因为在文件迭代开始时,!feof(file) 为真,因此尝试了fread,但失败了,然后将size 设置为i + 1,即使没有读取任何字节。

因为feof 是这样工作的,所以你不能用它来控制这样的循环。相反,您应该编写循环,以便它测试fread 的结果,如果它无法读取任何字符,代码将退出循环。该代码可能类似于:

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

(请注意,如果您使用fread 一次读取多个字节,则需要额外的代码来处理读取的字节数介于零和请求的数字之间的情况。)

修复该循环后,您应该会看到流中报告的字符数为 75。很可能,您的文件 f.txt 包含三行文本,总共 72 个字符,不包括行尾。读为文本流时,有3个'\n'字符,共75个。读为二进制流时,有3个'\n'字符和3个'\r'字符,共78个。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-30
    • 1970-01-01
    • 2011-09-27
    相关资源
    最近更新 更多