我正在为我的学校做一个项目,但我找不到如何获取文件的大小。由于我需要读取脚本并在程序中使用它,因此我需要文件大小才能使用读取或读取。
这是我为获取文件大小所做的工作,但它似乎不起作用。
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() 作为这个项目的规则,也不能使用任意大小(如 100)的 read 或 fread,因为给出的脚本可以小或大。
如果你可以依赖输入是一个持久文件(即驻留在存储介质上),并且该文件在程序运行期间没有被修改,那么你可以预先读取它到最后以计算其中的字节数,然后倒带。
但在学术练习之外,禁止通过stat()
、fseek()
和类似方式测量大小的通常原因是输入可能不驻留在存储介质上,因此
- 如果不阅读它,您就无法确定其大小,而且
- 你无法倒带它或在其中寻求。
那么诀窍不是如何提前确定尺寸,而是如何在不提前测量尺寸的情况下做到这一点。 至少有两种主要策略:
-
首先不要依赖将所有内容一次性存储在内存中。 相反,在读取其内容时对其内容进行操作,在任何给定时间仅保留足够的内存。
-
或者,动态适应文件大小。 对此有很多变化。例如,如果您只是将文件读入整体块,那么您可以
malloc()
空间并在发现需要更多时realloc()
。 或者,您可以将内容存储在链表中,根据需要分配新的列表节点。
至于问题中提出的方法,有几个问题。 这似乎是尝试按照我第一次描述的那样 - 将文件读取到最后以确定其大小 - 但是
-
它似乎假设每个
read()
都将从文件的开头开始,或者如果read()
无法读取整个文件,则可能会失败。 事实并非如此。 每个read()
都将从文件的当前位置开始,并将文件保留在传输的最后一个字节之后。 -
因为它改变了文件的位置,所以您的方法将要求文件在之后倒带 - 例如通过
lseek()
。 但是,如果lseek()
可以用于此目的(并注意我之前关于您无法在其中查找的文件的评论),那么它将提供一种更干净的方法来测量文件大小。 -
您不考虑 I/O 错误。 如果发生了一个,那么它可能会将你的程序发送到一个无限循环。
-
动态分配相对昂贵,而且您正在做很多事情。 如果你想实现预读策略,那么这将是一个更好的实现:
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; }
在可执行文件上使用gdb
调试器或 strace(1),使用所有警告和调试信息进行编译:gcc -Wall -Wextra -g
使用 GCC。仔细阅读 read(2) 的文档,以及你正在使用的每个函数的文档(包括 malloc(3),你忘记测试它的失败)。
你需要使用 read(2) 的结果(实际读取字节数)。并且您需要专门处理错误情况(当read
给出 -1 时)。
对于足够长的文件,可能发生的情况是,在第一个循环中,您正在读取 1 个字节,在第二个循环中您正在读取 2 个字节,在第三个循环中您读取了 3 个字节,依此类推......(在这种情况下,您忘记计算 1+2+3)。
你应该对所有read_output
进行累积和求和,并且当read(2)给出的小于size
时,你应该处理这种情况(这应该发生在你的read
最后一次给出非零时)。
相反,我建议使用固定缓冲区(常量或固定大小),并重复执行 read(2),但小心使用返回的字节计数(此外,处理错误和 EOF 条件)。
请注意,系统调用(在系统调用(2)中列出)非常昂贵。根据经验,您应该读取(2)或写入(2)几千字节的缓冲区(并仔细处理返回的字节计数,并针对错误对其进行测试,请参阅errno(3))。每次一次只read
几个字节的程序效率低下。
此外,malloc
(或realloc
)相当昂贵。将堆分配的大小增加 1 是丑陋的(因为您在每个循环中都调用malloc
;在您的情况下,您甚至不需要使用malloc
)。你最好使用一些几何级数,也许是newsize = 4*oldsize/3 + 10;
(或类似)。