C 文件大小差异

标签 c filesize file-pointer

我正在尝试学习 C,目前正在编写玩具脚本。 现在,它只是打开一个文本文件,一个字符一个字符地读取它,然后 将其吐出到命令行上。

我查看了如何查看文件的大小(使用 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 Æ

由于为字符串分配了太多内存,打印的尾部似乎是垃圾。

在此先感谢您提供的任何帮助/回答!

最佳答案

如果您运行的是 Windows:

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

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

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

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

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

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

当然,这会浪费空间并且在文本中使用 \r 字符不是很方便,因此您可以像现在这样分配,最后执行 realloc 用实际字符数缩小分配的内存(因为它更小,没关系)

stringOfFile = realloc(stringOfFile,i+1);

请注意,由于我已经考虑到需要添加空终止符,所以我在字符数中添加了 1,因此如果没有任何 \r 字 rune 件,realloc 可以将 block 的大小增加 1。

所以,正如我所暗示的,不要忘记以 nul 终止您的字符串,否则 printf 不会正确停止:

stringOfFile[i] = '\0';

(除非您不关心创建 C 字符串,因为存储字符串大小 + 逐字符显示也是正确的)

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

在一般情况下,最好:

  • 分配一个小缓冲区
  • 逐个读取一个字符并存储
  • 如果缓冲区已满,调用 realloc 将大小增加一些(不是在每个字符上,性能会很差)
  • 最后再次调用realloc来更精确的调整大小

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

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

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

关于C 文件大小差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50154719/

相关文章:

php - 如何在PHP扩展中捕获上传的文件数据

c - 如何获取C盘操作系统信息

delphi - Winapi.ShLwApi.StrFormatByteSize64 将我的应用程序视为 DLL

c - 在 C 中读取文件内容,跟踪新行

c++ - 为什么在传递字符串作为文件名而不是 char* 时会出现错误?

c - 动态分配文件指针数组

c - 与不同的 header 共享数据结构

c - 为什么数据不保存到结构中?

sql-server - 使用 SQL 获取文件夹内每个文件的大小

delphi - 使用批处理脚本检测文件损坏