C文件大小不一致

时间:2018-01-08 20:55:03

标签: c filesize file-pointer

我正在尝试学习C,目前正在制作一个玩具脚本。 现在,它只是打开一个文本文件,通过char读取它,并且 将它吐出到命令行。

我查看了如何查看文件的大小(使用fseek()然后使用ftell()), 但是当我遍历文件时,它返回的结果与我在while循环中计算字符所获得的数字不匹配。

我想知道这种差异是由于Windows使用\ r \ n而不仅仅是\ n,因为差异似乎是#newlines + 1。

以下是我正在处理的脚本:

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

int main()
{
        FILE * fp = fopen("test.txt", "r");

        fseek(fp, 0, SEEK_END);
        char * stringOfFile = malloc(ftell(fp));
        printf("allocated %d characters for file\n", ftell(fp));
        fseek(fp,0,SEEK_SET);//reset pointer

        char tmp = getc(fp); //current letter in file
        int i=0;
        while (tmp != EOF) //End-Of-File (defined in stdio.h)
        {
                *(stringOfFile+i) = tmp;
                tmp = getc(fp);
                i++;
        }
        fclose(fp);
        printf("Turns out we had %d characters to store.\nThe file was as follows:\n", i);
        printf("%s", stringOfFile);
}

我得到的输出(你可以从输出中看到一个简单的测试文件):

allocated 67 characters for file
Turns out we had 60 characters to store.
The file was as follows:
line1
line2
line3
line4
line5
(last)line6

lmnopqrstuvw▬$YL Æ

其中打印的尾部比特似乎是垃圾而不是为字符串分配太多内存。

提前感谢您提供的任何帮助/答案!

2 个答案:

答案 0 :(得分:3)

如果您正在运行Windows:

FILE * fp = fopen("test.txt", "r");

text 模式打开文件,这意味着\r\n转换为\n

因此,如果您的文件有7行,则转换将删除7个字符(即,如果文件使用Windows样式的行终止)

修复是以二进制模式打开它

FILE * fp = fopen("test.txt", "rb");

所以ftell和逐个阅读字符应匹配。

当然,这是浪费空间和在你的文本中使用\r字符不是很方便,所以你可以像你正在做的那样进行分配,并最终执行realloc以使用实际的字符数减少分配的内存(从那以后)它更小,没关系)

stringOfFile = realloc(stringOfFile,i+1);

请注意,由于我已经考虑了添加nul-terminator的需要,因此我在chars的数量上加了1,所以如果文件中没有\r个字符,那么realloc可以将块的大小增加1。

所以,正如我所暗示的那样,不要忘记终止你的字符串或printf没有正确停止:

stringOfFile[i] = '\0';

(除非你不关心创建一个C字符串,因为存储字符串大小+显示char-by-char也是正确的)

我们已经看到ftell方法很棘手,在某些情况下,当流是例如命令的输出时(popen返回FILE *但你不能fseek它或者套接字,无论如何,由于我们事先不知道数据的大小,所以不能应用这个原则。

在一般情况下,最好是:

  • 分配一个小缓冲区
  • 通过char和store
  • 读取char
  • 如果缓冲区已满,请调用realloc以增加一些步骤的大小(不是每个字符,性能都会很差)
  • 最后再次致电realloc以更精确地调整尺寸

(它也透明地解决了二进制/文本问题)

请注意,如果您使用的是大型文件(> 4GB),则必须使用64位无符号整数作为位置和fopen64种I / O函数(以及所有偏移变量,如{{1}应该是unsigned /符合i的返回类型,否则你将开始遇到2GB的问题。好吧,我想在处理适度小的文本文件时并不重要。

另外,检查大卫回答。对于文本文件,将ftell的结果放在getc中应该有效,但在二进制文件的一般情况下则不行。

答案 1 :(得分:1)

    char tmp = getc(fp); //current letter in file
    int i=0;
    while (tmp != EOF) //End-Of-File (defined in stdio.h)

您需要检查getcEOF返回的值。相反,您将其转换为char,然后检查是否等于EOF转换为char。但是如果转换为char的{​​{1}}的值实际上在文件中呢?检查文档,EOF返回getc

你也有其他错误。