首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >是否可以在不将文件加载到内存的情况下读取文件?

是否可以在不将文件加载到内存的情况下读取文件?
EN

Stack Overflow用户
提问于 2018-01-02 17:49:44
回答 2查看 4.1K关注 0票数 1

我想读取一个文件,但是它太大了,无法将它完全装入内存中。

有没有一种不用把它装入内存就能读取它的方法?还是有更好的解决方案?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-01-02 20:23:42

我需要内容来做校验和,所以我需要完整的消息。

许多校验和库支持对校验和的增量更新。例如,GLib有g_checksum_update()。因此,您可以使用fread一次读取一个块文件,并在读取时更新校验和。

代码语言:javascript
运行
复制
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <glib.h>

int main(void) {
    char filename[] = "test.txt";

    // Create a SHA256 checksum
    GChecksum *sum = g_checksum_new(G_CHECKSUM_SHA256);
    if( sum == NULL ) {
        fprintf(stderr, "Could not create checksum.\n");
        exit(1);
    }

    // Open the file we'll be checksuming.
    FILE *fp = fopen( filename, "rb" );
    if( fp == NULL ) {
        fprintf(stderr, "Could not open %s: %s.\n", filename, strerror(errno));
        exit(1);
    }

    // Read one buffer full at a time (BUFSIZ is from stdio.h)
    // and update the checksum.    
    unsigned char buf[BUFSIZ];
    size_t size_read = 0;
    while( (size_read = fread(buf, 1, sizeof(buf), fp)) != 0 ) {
        // Update the checksum
        g_checksum_update(sum, buf, (gssize)size_read);
    }

    // Print the checksum.
    printf("%s %s\n", g_checksum_get_string(sum), filename);
}

我们可以通过与sha256sum的结果进行比较来检验它的工作性能。

代码语言:javascript
运行
复制
$ ./test
0c46af5bce717d706cc44e8c60dde57dbc13ad8106a8e056122a39175e2caef8 test.txt
$ sha256sum test.txt 
0c46af5bce717d706cc44e8c60dde57dbc13ad8106a8e056122a39175e2caef8  test.txt
票数 3
EN

Stack Overflow用户

发布于 2018-01-02 18:27:16

如果问题是内存而不是虚拟地址空间,那么其中一种方法是内存映射文件,或者在POSIX系统上映射mmap,或者在CreateFileMapping上映射文件。

这可以获得文件字节的原始数组,但是操作系统负责在执行过程中分页内容(如果您更改它们,则将它们写回磁盘)。映射为只读时,它非常类似于内存块malloc-ing和填充它的fread-ing,但是:

  1. 这很懒惰:对于一个1GB文件,在使用它的任何部分之前,您不需要等待5-30秒的时间来读取整个文件,相反,您只需在访问时为每个页面付费(有时,操作系统会在后台预读,因此您甚至不必等待每页加载)。
  2. 它在内存压力下的响应更好;如果内存不足,操作系统就可以从内存中删除干净的页,而无需将它们写入交换,因为它知道可以在需要时从文件中的黄金副本中将它们重新分页;对于malloc-ed内存,它必须将其写出来以进行交换,从而增加磁盘上可能已经超额订阅的磁盘流量。

就性能而言,在默认设置下,这可能会稍微慢一些(因为在没有内存压力的情况下,读取整个文件可以保证当请求时它将在内存中,而随机访问内存映射的文件可能会触发按需页错误以在第一次访问时填充每个页面),尽管您可以使用posix_madvisePOSIX_MADV_WILLNEED (POSIX系统)或PrefetchVirtualMemory (Windows 8及更高版本)一起提供整个文件将被需要的提示,从而导致系统(通常)在后台(通常)在访问它时将其分页。在POSIX系统上,当不需要(或可能)同时分页整个文件时,其他advise提示可以用于更细粒度的提示,例如,如果您从头到尾读取文件数据,则使用POSIX_MADV_SEQUENTIAL通常会触发对后续页面的更积极的预取,增加它们在到达时在内存中的可能性。通过这样做,您可以获得两个世界的优势;您几乎可以立即开始访问数据,延迟访问尚未被传呼的页面,但是操作系统将在后台为您预装页面,因此您最终将以最快的速度运行(同时仍然能够更好地抵御内存压力,因为操作系统只会丢弃干净的页面,而不是先编写它们以进行交换)。

这里的主要限制是虚拟地址空间。如果您使用的是32位系统,则很可能被限制在(取决于现有地址空间的碎片化程度) 1-3 GB的连续地址空间,这意味着您必须以块的形式映射该文件,并且不能在任何时候按需随机访问文件中的任何点而不需要额外的系统调用。值得庆幸的是,在64位系统上,这种限制很少出现;即使是最限制的64位系统(Windows 7)也为每个进程提供了8 TB的用户虚拟地址空间,远远大于您可能遇到的绝大多数文件(后来的版本将上限提高到128 TB)。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48065547

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档