在C语言中读取任意长度文件的最惯用/最有效的方法是什么?
import csv
d = {"(2,3,4)": '3', "(201,233,207)": '23', "(176,247,207)": '78'}
with open("data.csv", "w", newline='') as f:
w = csv.writer(f, delimiter='\t')
w.writerow(map(str, (0, 'xval', 'yval')))
for counter, (key, val) in enumerate(d.items(), 1):
w.writerow(map(str, (counter, key, val)))
fread()
恒定大小的缓冲区,直到获得EOF 答案 0 :(得分:2)
避免使用任何需要事先知道文件大小的技术。剩下的只是一种技术:一次读取文件,大小适中,一次读取一次。
这就是为什么您不想尝试提前查找文件大小的原因:
如果它不是常规文件,则可能无法分辨。例如,您可能直接从控制台读取数据,或者从先前的数据生成器获取管道输入。如果您的程序要求文件大小是可理解的,则这些有用的输入机制将对您的用户不可用,这些用户会抱怨或选择其他工具。
即使您可以弄清文件大小,也无法防止在读取文件时更改文件大小。如果您不小心阅读文件,则可能会打开一个漏洞,该漏洞可能会被对抗性程序利用。
例如,如果您分配“正确”大小的缓冲区,然后读取直到出现文件结束条件,则最终可能会覆盖随机存储器。 (如果使用read()
之类的接口来读取的数据可能少于所请求的数据,则可能需要多次读取。)或者您可能会发现文件已被截断;如果不检查读取的数据量,则可能会处理未初始化的内存,从而导致信息泄漏。
答案 1 :(得分:1)
实际上,通常不需要将整个文件内容保留在内存中。您通常会parse使用文件(尤其是文本格式的文件),或者至少要以较小的片段读取文件,因此,您不需要在内存中完全使用它。对于文本文件,逐行读取(可能在解析器内部处于某种状态)通常就足够了(使用fgets或getline)。
Files之所以存在(特别是在disks或SSD上)是因为它们通常比您的计算机RAM更大。实际上,已经发明了文件(超过50年前),以便能够处理大于内存的数据。 Distributed file systems也可能很大(甚至可以从笔记本电脑远程访问,例如NFS,CIFS等)
某些file systems能够存储PB级的数据(在超级计算机上),并且具有几个TB级的单个文件(比可用RAM大得多)。
您还可能会使用一些database。这些通常具有TB级的数据。另请参见this的答案(大约sqlite
个数据库的实际大小)。
如果您真的想使用stdio完全读取内存中的文件(但是您应该避免这样做,因为您通常希望程序能够处理文件中的大量数据;因此读取内存中的整个文件就是通常是设计错误),您确实可以在fread(或fscanf,甚至fgetc)上循环,直到文件结束。请注意,feof仅在某些输入操作之后 有用。
在当前的便携式计算机或台式计算机上,(为了提高效率)您可能更喜欢使用几兆字节的缓冲区,并且您当然可以处理几百千兆字节(比您的RAM大得多)的大文件。
在POSIX文件系统上,您可以使用以下命令进行memory mapped IO: mmap(2)-但这可能不会比具有大缓冲区(几兆字节)的read(2)快。您可以使用readahead(2)(特定于Linux)和posix_fadvise(2)(如果使用mmap
,则可以使用madvise(2)),通过提示操作系统kernel来调整性能。
如果必须为Microsoft Windows编写代码,则可以研究其WinAPI和find进行内存映射IO的某种方式。
实际上,文件数据(尤其是最近访问过的文件数据)通常保留在page cache中,这对性能至关重要。如果不是这种情况,则您的硬件(磁盘,控制器,...)将成为瓶颈,而程序将成为I/O bound(在这种情况下,没有任何软件技巧可以显着提高性能)。