【问题标题】:Letting a `std::vector<unsigned char>` steal memory from a `std::string`让 `std::vector<unsigned char>` 从 `std::string` 窃取内存
【发布时间】:2018-12-02 22:03:30
【问题描述】:

假设我们有 std::string s 保存一个原始数据缓冲区,但我们想要 std::vector&lt;uint8_t&gt; v 代替。缓冲区长度以百万计。有没有简单的方法让v 窃取s 的内存,从而避免复制缓冲区?

就像 std::vector&lt;uint8_t&gt;::vector(std::string&amp;&amp;) 会做的那样,但以某种方式从 STL 外部做。

或者,是否可以通过与ss.str() 一样高效的操作从std::stringstream ss 获取v

【问题讨论】:

  • 如果向量和字符串都有自定义分配器,你也许可以做类似的事情
  • 为什么不首先使用向量作为缓冲区?
  • @Museful 为什么不直接使用fread
  • 哦,那很容易——获取文件的长度——见stackoverflow.com/questions/5840148/…,分配一个合适大小的向量并读入它。但我认为你对所谓的“意图”太着迷了。字符串和字节向量实际上是等价的。
  • std::strings 处理“二进制”数据就好了。

标签: c++ stdvector move-semantics stringstream rvalue-reference


【解决方案1】:

好的,那里有很多 cmet,让我们试着把一些东西放在一起,因为我需要练习并且可能会获得一些积分 [更新:没有 :(]。

我对“现代 C++”还很陌生,所以请在您找到时接受它。可能需要到 C++17,我没有仔细检查过。任何批评都非常受欢迎,但我更愿意编辑我自己的帖子。请记住,在阅读本文时,OP 实际上 想要做的是从文件中读取他的字节。谢谢。

更新:根据@Deduplicator 下面的评论,调整以处理在对stat() 的调用和对fread() 的调用之间文件大小发生变化的情况...并随后替换@ 987654325@ 和 std::ifstream,我想我们已经到了。

#include <string>
#include <vector>
#include <optional>
#include <iostream>
#include <fstream>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>

using optional_vector_of_char = std::optional <std::vector <char>>;

// Read file into a std::optional <std::vector <char>>.
// Now handles the file size changing when we're not looking.
optional_vector_of_char SmarterReadFileIntoVector (std::string filename)
{
    for ( ; ; )
    {
        struct stat stat_buf;
        int err = stat (filename.c_str (), &stat_buf);
        if (err)
        {
            // handle the error
            return optional_vector_of_char ();   // or maybe throw an exception
        }

        size_t filesize = stat_buf.st_size;

        std::ifstream fs;
        fs.open (filename, std::ios_base::in | std::ios_base::binary);
        if (!fs.is_open ())
        {
            // handle the error
            return optional_vector_of_char ();
        }

        optional_vector_of_char v (filesize + 1);
        std::vector <char>& vecref = v.value ();
        fs.read (vecref.data (), filesize + 1);

        if (fs.rdstate () & std::ifstream::failbit)
        {
            // handle the error
            return optional_vector_of_char ();
        }

        size_t bytes_read = fs.gcount ();
        if (bytes_read <= filesize)              // file same size or shrunk, this code handles both
        {
            vecref.resize (bytes_read);
            vecref.shrink_to_fit ();
            return v;                            // RVO
        }

        // File has grown, go round again
    }
}    

int main ()
{
    optional_vector_of_char v = SmarterReadFileIntoVector ("abcde");
    std::cout << std::boolalpha << v.has_value () << std::endl;
}

Live demo。当然没有可读取的实际文件,所以...


另外:您是否考虑过编写自己的简单容器来映射文件视图?只是一个想法。

【讨论】:

  • 有一个明显的 TOCTOU 错误,因为文件可以在 stat 和 open 之间改变长度。另外,您为什么不想在非 Windows 上读取二进制文件,因为根据您的评论,这似乎是正确的?
  • @Deduplicator 确实,我很傻,虽然听起来它不适用于 OP。我已经相应地修改了代码。至于第 2 点,二进制文件和文本文件在 Windows 以外的平台上没有区别,因为不需要翻译行尾。为 mode 参数指定 "rb" 或 "rt") 是 Windows 扩展。
  • b 不是 Windows 扩展,即使它在 Unix 上什么也不做,但在早期的系统中二进制文件和文本文件是不同的。另见en.cppreference.com/w/c/io/fopen
  • @Deduplicator 好的,我看到了 可以选择指定文件访问模式标志“b”以二进制模式打开文件。此标志对 POSIX 系统没有影响,但在 Windows 上,它会禁用对“\n”和“\x1A”的特殊处理。我担心某些平台可能会反对 "rb"。无论如何,现在没有实际意义,我将代码更改为使用ifstream,这样更好。
猜你喜欢
  • 2012-03-07
  • 2012-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-11
  • 1970-01-01
  • 2011-07-23
相关资源
最近更新 更多