通过读取文件创建 map<string,vector<string>> 时如何管理内存

Posted

技术标签:

【中文标题】通过读取文件创建 map<string,vector<string>> 时如何管理内存【英文标题】:How is memory managed when creating a map<string,vector<string> > by reading files 【发布时间】:2019-01-16 09:09:49 【问题描述】:

我想知道当不同的文件存储在字符串向量映射中时如何管理内存。 我试图读取每个 10 个月的不同文件以将它们放入内存中,当我使用 KSySGuard 检查内存时,出现的内存是我文件内存的两倍多(约 70 个月)。 我给你一个代码示例: 有一个函数readfile():

std::vector<std::string> read_file(std::string& path)
    ifstream fichier(path);
    std::vector<std::string> fich;
    if(fichier)
       string ligne;
        while(getline(fichier, ligne))
           fich.push_back(ligne);
        
     
    fichier.close();
    return fich;

这个函数在另一个正在构建我的地图中使用:

std::map<std::string, std::vector<std::string>> buildmap(std::string folder)
    std::map<std::string,std::vector<std::string>> evaluations; std::vector<std::string> vecFiles = "file1","file2","file3";
    for( auto i = 0; i < vecFiles.size(); i++ )
    
        std::stringstream strad;
        strad <<vecFiles[i] ;
        std::string path(folder+vecFiles[i]);
        std::vector<std::string> a = read_file(path);
        evaluations[strad.str()]=a;
    
    return evaluations;   

所以,我不明白为什么内存与文件大小相比如此之高。有没有更有效的方法来构建这种容器?

【问题讨论】:

与您的问题完全无关,但为什么字符串流strad?为什么不直接使用vecFile[i]?您甚至不需要 path 变量(如果您让 read_file 取而代之的是 const 引用,那么您确实应该这样做)。 Why is vector array doubled?的可能重复 std::vector&lt;std::string&gt; a = read_file(path); evaluations[strad.str()]=a; 这是一个副本,你可以移动它(实际上你使用移动插入代替)。 一摞纸在抽屉里不会占用太多空间,但要找到你想要的那张却非常慢。为有效定位而整理的一堆文件将占用更多空间。 A std::string 会有一些开销。文件中的很多行意味着很多 std::strings 和更多开销。您应该可以通过使用 std::vector&lt;char&gt; 来减少一些开销,但我不太确定这是您想要做的事情。 【参考方案1】:

您的场景中有很多内存开销:

    您将每个文件行存储为单独的std::string 对象。每个这样的对象本身都会占用一些空间(在 64 位架构上通常为 24 或 32 个字节),但是,存储的字符串(行字符)仅在字符串很短且小/短字符串优化 (SSO) 时才存储在其中应用(通常是来自 C++11 的常见标准库实现)。如果行很长,字符串的空间是动态分配的,每次分配也会有一些额外的内存开销。 您将push_back 这些std::string 对象放入std::vector,这通常会以指数方式增加内部缓冲区的大小(例如在空间不足时将其翻倍)。这就是为什么在您预先知道向量元素的数量时使用保留空间 (std::vector::reserve)。

这是这种“舒适”方法的代价。可能有帮助的是将整个文件内容存储为单个std::string,然后仅将索引/指针存储到单独数组/向量中各个行的开头(尽管您不能将这些指针视为字符串,因为它们不会以空结尾;或者,实际上,如果您用空字符替换换行符,则可以)。

在 C++17 中,您可以将行作为 std::string_view 的实例存储到存储在单个 std::string 中的整个文件内容中。

请注意,std::string_view 可能会比指针/索引大。例如,对于 libstdc++ 和 x86_64,sizeof(std::string_view) 是 16 个字节,但指针/索引将占用 8 个字节。对于小于 4 GB 的文件,您甚至可以使用 32 位索引。如果处理的文件中有很多行,这些差异可能很重要。

更新

这个问题高度相关:C++ Fast way to load large txt file in vector。

【讨论】:

我会尝试实施这些建议,如果可行,我会接受答案。如果您有一个示例,主要针对第 2 点)(我不确定第 1 点)会释放大量内存,因为我没有很多文件),那就太好了 @froz 第 1 点)也可能节省大量内存,可能比第 2 点要多得多)。我会将整个文件读入单个std::string,在计算行数时用空字符替换换行符,然后使用该计数器为向量索引保留空间,最后再次传递并存储索引。这 2 个结构(带有文件内容的字符串和索引向量)将是 read_file 函数的结果。 @froz 顺便说一句,重要的不是文件数量,而是每个文件中的行数。

以上是关于通过读取文件创建 map<string,vector<string>> 时如何管理内存的主要内容,如果未能解决你的问题,请参考以下文章

Java servlet 将Map传到jsp中,通过<c:forEach >怎么将map中的值读取到?

java语言如何循环读取Map对象内的数据

uva-10887-枚举

Dictionary对象可以通过VALUE读取KEY吗

Properyies文件的读取

使用 ++ 运算符增加 map<string, int>