在C中读取任意长度的文件
Reading a file of arbitrary length in C
在 C 中读取任意长度文件最idiomatic/efficient的方法是什么?
- 获取文件的文件大小(以字节为单位)并发出单个
fread()
- 保持
fread()
一个恒定大小的缓冲区,直到得到 EOF
- 还有什么吗?
实际上,您通常不需要将整个文件内容保存在内存中。你会经常 parse the file (notably if it is textual), or at least read the file in smaller pieces, and for that you don't need it entirely in memory. For a textual file, reading it line-by-line (perhaps with some state inside your parser) is often enough (using fgets or getline).
Files exist (notably on disks or SSDs) because usually they can be much "bigger" than your computer RAM. Actually, files have been invented (more than 50 years ago) to be able to deal with data larger than memory. Distributed file systems also can be very big (and accessed remotely even from a laptop, e.g. by NFS, CIFS, 等等...)
一些 file systems 能够存储数 PB 的数据(在超级计算机上),单个文件数为数 TB(比可用 RAM 大得多)。
您还可能会使用一些 databases. These routinely have terabytes of data. See also this answer (about realistic size of sqlite
数据库)。
如果你真的想使用 stdio 完全读取内存中的文件(但你应该避免这样做,因为你通常希望你的程序能够处理文件中的大量数据;所以读取整个文件内存通常是设计错误),你确实可以在 fread (or fscanf, or even fgetc) till end-of-file. Notice that feof 上循环,仅在 一些输入操作后 有用。
在当前的笔记本电脑或台式计算机上,您可能更喜欢(为了效率)使用几兆字节的缓冲区,并且您当然可以处理数百 GB 的大文件(比您的 RAM 大得多)。
在 POSIX 文件系统上,您可以 memory mapped IO with e.g. mmap(2) - but that might not be faster than read(2) with large buffers (of a few megabytes). You could use readahead(2) (Linux specific) and posix_fadvise(2) (or madvise(2) if using mmap
) to tune performance by hinting your OS kernel.
如果您必须为 Microsoft Windows 编写代码,您可以研究其 WinAPI and find 一些执行内存映射 IO 的方法。
实际上,文件数据(尤其是最近访问过的文件)通常保留在 page cache, which is of paramount importance for performance. When that is not the case, your hardware (disk, controller, ...) becomes the bottleneck and your program becomes I/O bound 中(在这种情况下,没有任何软件技巧可以显着提高性能)。
避免使用任何需要提前知道文件大小的技术。这只剩下一种技术:一次读取文件,以方便大小的块为单位。
以下是您不想尝试提前查找文件大小的原因:
如果不是普通文件,可能就没办法分辨了。例如,您可能直接从控制台读取数据,或者从以前的数据生成器获取管道输入。如果您的程序要求文件大小是可知的,这些有用的输入机制将对您的用户不可用,他们会抱怨或选择不同的工具。
即使您可以计算出文件大小,也无法阻止它在您读取文件时发生变化。如果您不注意读取文件的方式,您可能会打开一个漏洞,该漏洞可能会被敌对程序利用。
例如,如果您分配一个 "correct" 大小的缓冲区,然后读取直到遇到文件结束条件,您可能最终会覆盖随机内存。 (如果您使用像 read()
这样的接口可能读取的数据少于请求的数据,则可能需要多次读取。)或者您可能会发现文件已被截断;如果您不检查读取的数据量,您可能最终会处理未初始化的内存,从而导致信息泄漏。
在 C 中读取任意长度文件最idiomatic/efficient的方法是什么?
- 获取文件的文件大小(以字节为单位)并发出单个
fread()
- 保持
fread()
一个恒定大小的缓冲区,直到得到 EOF - 还有什么吗?
实际上,您通常不需要将整个文件内容保存在内存中。你会经常 parse the file (notably if it is textual), or at least read the file in smaller pieces, and for that you don't need it entirely in memory. For a textual file, reading it line-by-line (perhaps with some state inside your parser) is often enough (using fgets or getline).
Files exist (notably on disks or SSDs) because usually they can be much "bigger" than your computer RAM. Actually, files have been invented (more than 50 years ago) to be able to deal with data larger than memory. Distributed file systems also can be very big (and accessed remotely even from a laptop, e.g. by NFS, CIFS, 等等...)
一些 file systems 能够存储数 PB 的数据(在超级计算机上),单个文件数为数 TB(比可用 RAM 大得多)。
您还可能会使用一些 databases. These routinely have terabytes of data. See also this answer (about realistic size of sqlite
数据库)。
如果你真的想使用 stdio 完全读取内存中的文件(但你应该避免这样做,因为你通常希望你的程序能够处理文件中的大量数据;所以读取整个文件内存通常是设计错误),你确实可以在 fread (or fscanf, or even fgetc) till end-of-file. Notice that feof 上循环,仅在 一些输入操作后 有用。
在当前的笔记本电脑或台式计算机上,您可能更喜欢(为了效率)使用几兆字节的缓冲区,并且您当然可以处理数百 GB 的大文件(比您的 RAM 大得多)。
在 POSIX 文件系统上,您可以 memory mapped IO with e.g. mmap(2) - but that might not be faster than read(2) with large buffers (of a few megabytes). You could use readahead(2) (Linux specific) and posix_fadvise(2) (or madvise(2) if using mmap
) to tune performance by hinting your OS kernel.
如果您必须为 Microsoft Windows 编写代码,您可以研究其 WinAPI and find 一些执行内存映射 IO 的方法。
实际上,文件数据(尤其是最近访问过的文件)通常保留在 page cache, which is of paramount importance for performance. When that is not the case, your hardware (disk, controller, ...) becomes the bottleneck and your program becomes I/O bound 中(在这种情况下,没有任何软件技巧可以显着提高性能)。
避免使用任何需要提前知道文件大小的技术。这只剩下一种技术:一次读取文件,以方便大小的块为单位。
以下是您不想尝试提前查找文件大小的原因:
如果不是普通文件,可能就没办法分辨了。例如,您可能直接从控制台读取数据,或者从以前的数据生成器获取管道输入。如果您的程序要求文件大小是可知的,这些有用的输入机制将对您的用户不可用,他们会抱怨或选择不同的工具。
即使您可以计算出文件大小,也无法阻止它在您读取文件时发生变化。如果您不注意读取文件的方式,您可能会打开一个漏洞,该漏洞可能会被敌对程序利用。
例如,如果您分配一个 "correct" 大小的缓冲区,然后读取直到遇到文件结束条件,您可能最终会覆盖随机内存。 (如果您使用像
read()
这样的接口可能读取的数据少于请求的数据,则可能需要多次读取。)或者您可能会发现文件已被截断;如果您不检查读取的数据量,您可能最终会处理未初始化的内存,从而导致信息泄漏。