【问题标题】:how to read image files and store it in memory(std::string) in c++?如何读取图像文件并将其存储在 C++ 中的内存(std::string)中?
【发布时间】:2012-03-25 14:37:05
【问题描述】:

我今天大部分时间都在网上研究,但找不到答案,所以我转向 stackoverflow 寻求一些建议。

基本上,我有一个 c++ 库,它使用 curl 执行 PUT 方法来上传图像文件。现在这个库需要一个 std::string 作为数据。我的本地磁盘上有图像文件(如 jpg、gif、png)。

我不关心文件的内容(例如,除了将它传递给这个库以用于 PUT 方法之外,我什么也不做)。如何读取图像文件并将其存储在 std::string 中?如果文件内容包含 NULL 终止符怎么办?

我尝试添加一些我尝试过的代码,但我是新来的,我不确定如何以正确的格式将我的代码粘贴到这里。任何帮助将不胜感激。

【问题讨论】:

  • 要在此处粘贴您的代码,只需将所有内容缩进 4 个空格。或者,粘贴您的代码,选择它,然后按下如下所示的按钮:{}。您还可以通过用小东西包围它来将代码嵌入到您的文本中。我认为它们被称为反引号。在我用过的每个键盘上,它都是左上角的键,在 1 键的左侧。
  • @Benjamin:在德语键盘布局中,它位于右上角,退格键的左侧。 :) 你还需要按 ctrl+alt 来获取它...无论如何,` 的意思。
  • 您使用的库真的需要字符串中的二进制数据吗?你确定这不是它想要的文件名?虽然char 类型在几乎所有平台上都是一个字节,但使用std::string 存储二进制数据对我来说似乎是错误的。

标签: c++ string image file binary


【解决方案1】:

我能想到的最简单的方法。以二进制模式打开文件,然后将整个文件读入stringstream

std::ifstream fin("foo.png", std::ios::in | std::ios::binary);
std::ostringstream oss;
oss << fin.rdbuf();
std::string data(oss.str());

【讨论】:

  • 这正是我所做的。但不起作用,因为它似乎只上传了一部分二进制数据。
【解决方案2】:
std::ifstream fin("foo.png", std::ios::binary);
std::string data;
data.reserve(1000000);
std::copy(std::istreambuf_iterator<char>(fin),
          std::istreambuf_iterator<char>(),
          std::back_inserter(data));

您可以使用这样的代码将图像文件读取到std::string。将reserve 方法的参数调整为大于文件大小的 99%。 ifstreamstring 都能正确处理零字节(称为 NULL 终止符)。


我找到了一个很好的article,其中比较了几种二进制文件加载方法。这是那篇文章中最快的方法:

std::ifstream fin("foo.png", std::ios::binary);
fin.seekg(0, std::ios::end);
std::string data;
data.resize(fin.tellg());
fin.seekg(0, std::ios::beg);
fin.read(&data[0], data.size());

这是最短的一个:

std::ifstream fin("foo.png", std::ios::binary);
std::string data((std::istreambuf_iterator<char>(fin)),
                 std::istreambuf_iterator<char>());

更新

这样的东西可以用来喂回调函数(我没有测试过):

std::ifstream fin("foo.png", std::ios::binary);
fin.seekg(0, std::ios::end);
...
curl_easy_setopt(m_ctx, CURLOPT_INFILESIZE,    fin.tellg());
curl_easy_setopt(m_ctx, CURLOPT_READDATA,      (void *)&fin);
fin.seekg(0, std::ios::beg);
...
static size_t put_callback(void *ptr, size_t size, size_t nmemb, void *data){
  std::ifstream* in = static_cast<std::ifstream*>(data);
  if(in->eof()) return 0;
  in->read((char *)ptr, size*nmemb);
  return in->gcount();
}

【讨论】:

  • 我昨天在我的代码上尝试了缩短版本,但这也没有用。我今天会尝试在这里获取我的代码,以便我可以在这方面获得更多帮助。
【解决方案3】:

这就是库进行 PUT 调用的方式,其中“数据”是作为文件内容传入的字符串

stringstream data_stream.str(data.c_str());
curl_easy_setopt(m_ctx, CURLOPT_UPLOAD,        1L);
curl_easy_setopt(m_ctx, CURLOPT_PUT,           1L);
curl_easy_setopt(m_ctx, CURLOPT_INFILESIZE,    data.length());
curl_easy_setopt(m_ctx, CURLOPT_READFUNCTION,  put_callback);
curl_easy_setopt(m_ctx, CURLOPT_READDATA,      (void *)&data_stream);
curl_easy_setopt(m_ctx, CURLOPT_WRITEFUNCTION, get_callback);
curl_easy_setopt(m_ctx, CURLOPT_WRITEDATA,     (void *)&m_request_response);

这里是 curlopt_readfunction 的回调函数

static size_t put_callback(void *ptr, size_t size, size_t nmemb, void *data){

  stringstream *output_stream;
  int          retval;

  output_stream = (stringstream *) data;

  if(output_stream->eof()) return 0;

  retval = min(size*nmemb,output_stream->str().size());
  output_stream->read((char *)ptr, retval);

  //return the number of bytes processed 
  return retval;
}

【讨论】:

  • 至少根据stackoverflow.com/questions/9612121/…,字符串应该能够存储零字节。嗯。
  • 是的,字符串可以存储零字节。该错误位于代码的第一行:data.c_str()。这里以零结尾的字符串作为 char 指针传递。因此字符串被第一个零字节修剪。要么删除不需要的c_str(),要么在回调中使用Benjamin 方法中的stringstream,或者你可以直接打开ifstream 并在回调中使用它(这种方法提供了更好的性能)。
  • 你是对的。我想我需要重写回调函数
  • 我更新了我的帖子来给你一些帮助。 (但我没有自己测试,因为没有安装 curl)。
  • evgeny,感谢您的更新。但是我无法在 curl 库中打开文件。我现在必须传入 std::string 。我已经测试过传递字符串流,它对我有用。现在我要回去使用 std::string 看看我是否可以做到这一点。
猜你喜欢
  • 1970-01-01
  • 2016-07-26
  • 1970-01-01
  • 2020-11-19
  • 2010-09-24
  • 2013-08-27
  • 2020-12-26
  • 2017-10-17
  • 2015-12-29
相关资源
最近更新 更多