如何在不使用fseek或stat的情况下在C中获取文件的大小?

时间:2018-01-02 15:33:15

标签: c file size

我正在为我的学校做一个项目,但我无法了解如何获得文件的大小。因为我需要读取一个脚本并在我的程序中使用它,所以我需要文件的大小来使用read或fread。

以下是我为获取文件大小所做的工作,但它似乎无法正常工作。

int my_size(int filedesc)
{
    int size = 1;
    int read_output = 1;
    char *buffer;

    for (size = 1; read_output != 0 ; size++) {
        buffer = malloc((size+1)*sizeof(char*));
        read_output = read(filedesc, buffer, size);
        free(buffer);
    }
    return(size);
}

并且我不允许使用stat()和fseek()作为此项目的规则,也不能使用任意大小的读取或fread,因为给出的脚本可能很小或很大。

2 个答案:

答案 0 :(得分:7)

如果您可以依赖输入作为持久性文件(即驻留在存储介质上),并且在程序运行期间未对该文件进行修改,那么您可以预先将其读取为结束计算其中的字节,然后倒带。

但在学术练习之外,禁止通过stat()fseek()及类似内容衡量大小的常见原因是输入可能不会驻留在存储媒体上,所以

  1. 如果不阅读它就无法确定其大小,还
  2. 你无法倒回或在其中寻找。
  3. 然后诀窍不在于如何提前确定尺寸,而是如何在没有的情况下提前测量尺寸。至少有两个主要策略:

    • 首先不要依赖于将整个内容一次存储在内存中。相反,在读取内容时对其内容进行操作,在任何给定时间内仅在内存中保留足够的内容。

    • 或者,动态调整文件大小。这有很多变化。例如,如果您只是将文件读入整体块中,那么当您发现需要更多时,可以malloc()空格和realloc()。或者您可以将内容存储在链接列表中,根据需要分配新的列表节点。

    至于问题中提出的方法,它有几个问题。这似乎是我第一次描述的尝试 - 将文件读到最后以确定其大小 - 但

    1. 似乎假设每个read()将从文件的开头开始,或者如果read()无法读取完整文件,则read()可能会失败。情况也不是这样。每个lseek()将从文件的当前位置开始,并将文件保留在最后一个字节传输之后。

    2. 因为它会更改文件位置,所以您的方法将要求文件在以后通过lseek()重绕。但是如果ssize_t count_bytes(int fd) { ssize_t num_bytes = 0; char buffer[2048]; ssize_t result; do { result = read(fd, buffer, sizeof(buffer)); if (result < 0) { // handle error ... } num_bytes += result; while (result > 0); return num_bytes; } 可以用于此目的(并且请注意我之前关于您无法搜索的文件的评论),那么它将提供一种更清晰的方法来测量文件的大小。

      < / LI>
    3. 您没有考虑I / O错误。如果发生了一个,那么它可能会将你的程序发送到一个无限循环。

    4. 动态分配相对昂贵,而且您正在做很多事情。如果您想实施预读策略,那么这将是一个更好的实现:

      attrName: DS.attr('type')

答案 1 :(得分:2)

使用您的可执行文件上的gdb debuggerstrace(1),使用GCC编译所有警告和调试信息:gcc -Wall -Wextra -g。仔细阅读read(2)的文档以及您正在使用的所有功能(包括malloc(3),其失败,您忘记了test)。

您需要使用read(2)的结果(实际读取字节数)。并且您需要特别处理错误情况(当read给出-1时)。

如果文件足够长,可能会发生的情况是,在第一个循环中,您正在读取1个字节,在第二个循环上读取2个字节,在第三个循环上读取3个字节,等等... (在这种情况下你忘了计算1 + 2 + 3)。

你应该累计并总结所有read_output,你应该处理read(2)给出的size以下的情况(这应该是你的read给出的最后一次非零)。

我建议使用 fixed 缓冲区(常量或固定大小),并重复执行read(2),但要小心使用返回的字节数(同样,处理错误和EOF)条件)。

请注意system calls(在syscalls(2)中列出)非常昂贵。根据经验,您应该read(2)write(2)一个几千字节的缓冲区(并仔细处理返回的字节数,并根据错误对其进行测试,请参阅errno(3))。程序read - 每次只有几个字节是低效的。

此外,malloc(或realloc)非常昂贵。将heap allocated大小增加一个是丑陋的(因为您在每个循环上调用malloc;在您的情况下,您甚至不需要使用malloc)。你最好使用一些几何级数,也许是newsize = 4*oldsize/3 + 10;(或类似的)。