【问题标题】:Compiling string and returning it as a vector编译字符串并将其作为向量返回
【发布时间】:2018-10-23 11:47:06
【问题描述】:

我的服务器收到一些请求,编译响应并将其发回。有时响应是一个文件,有时它是 HTML。所以我用以下方式定义响应:

class ResponseData
{

public:
    bool operator== (const ResponseData & param) const { return id == param.id; }

public:
    RequestIdType id;
    RequestType type;
    std::vector<char> data;
};

当我编译 HTML 响应时,我使用 std::stringstream

std::vector<char> RequestHandler::createResponse( const RequestData * request ) const
{
    std::stringstream buffer;

    std::vector<char> result;
    result.assign( RESPONSE_HEADER, RESPONSE_HEADER + strlen( RESPONSE_HEADER ) );

    buffer << "<tr>";


    for( auto param : request->paramsMap )
    {
        buffer << "<tr><td>" << param.first << "</td><td>" << param.second << "</td></tr>\n";
    }

    buffer << "<\tr>";

    DEBUG_LOG_F << buffer.str();

    std::string str = buffer.str();

    result.insert( result.end(), str.begin(), str.end() );
    result.insert( result.end(), RESPONSE_FOOTER, RESPONSE_FOOTER + strlen( RESPONSE_FOOTER ) );


    return result;
}

在我看来,将buffer 复制到字符串以将其附加到向量并不是一个好主意。我怎样才能更有效地做到这一点?

【问题讨论】:

  • 我建议进行以下优化。 1. 预先在结果中为所有内容预留空间。 2、从缓冲区中获取streambuf实例,然后使用其sgetn函数直接填充结果向量
  • 您是否打算获得两个深 &lt;tr&gt; 的结果?
  • @NishantSingh,好像你错过了几行
  • @Caleth,谢谢。当然不。但主要问题是关于有效地将字符串流复制到向量
  • @Yura srry 我更新了评论

标签: c++ string stl stringstream


【解决方案1】:

您可以使用std::istreambuf_iterator 将字符直接从stringstream 复制到向量中,如下所示:

std::vector<char> sstreamToVector(std::stringstream& src)
{
    // make sure that "get" position is at the beginning and "put" is at the end of the stream
    src.seekg(0);
    src.seekp(0, std::ios::end);

    std::vector<char> dst;
    dst.reserve(src.tellp());
    std::copy(std::istreambuf_iterator<char>(src),
              std::istreambuf_iterator<char>(),
              std::back_inserter(dst));
    return dst;
}

str.tellp() 返回写入流的字符数(p 代表“放置区”),因此可用于在缓冲区中分配足够的空间。

【讨论】:

  • 我可能会寻求结束并开始,首先,为了安全起见,否则?
  • @LightnessRacesinOrbit 好点,(+也许在最后恢复它们)应该在将它放在单独的函数中时完成
  • 哦,恢复是个好主意。有人会说,函数外部的光标状态应该遵守,应该在所有字节都“消耗”完之后在末尾。但由于我们从来没有真正从字符串流中消费任何东西(至少不是破坏性的),只要你记录你的选择,这并不重要。
【解决方案2】:

据我了解,您要避免的是chars 在容器之间的移动。在这种简单的情况下,直接转储result 中的所有内容并不难:

auto result = accumulate(cbegin(request->paramsMap), cend(request->paramsMap), vector<char>{ '<', 't', 'r', '>', '<', '/', 't', 'r', '>' }, [](auto& init, const auto& i) {
    const char start[] = { '<', 't', 'r', '>', '<', 't', 'd', '>' };
    const char middle[] = { '<', '/', 't', 'd', '>', '<', 't', 'd', '>' };
    const char finish[] = { '<', '/', 't', 'd', '>', '<', '/', 't', 'r', '>' };

    init.insert(prev(cend(init), 5U), cbegin(start), cend(start));
    init.insert(prev(cend(init), 5U), i.first, next(i.first, strlen(i.first)));
    init.insert(prev(cend(init), 5U), cbegin(middle), cend(middle));
    init.insert(prev(cend(init), 5U), i.second, next(i.second, strlen(i.second)));
    init.insert(prev(cend(init), 5U), cbegin(finish), cend(finsh));
    return init;
} );

copy(cbegin(result), cend(result), ostream_iterator<char>{ DEBUG_LOG_F });
result.insert(cbegin(result), RESPONSE_HEADER, next(RESPONSE_HEADER, strlen(RESPONSE_HEADER)));
result.insert(cend(result), RESPONSE_FOOTER, next(RESPONSE_FOOTER, strlen(RESPONSE_FOOTER)));
return result;

【讨论】:

  • 编译器如何防止这种情况发生? (提示:不会!)
  • 您的 XML 斜杠是反斜杠,您没有逃脱错误的反斜杠。
  • @LightnessRacesinOrbit 我曾希望编译器能在此处识别result 的构造并移动OP 的bufferRESPONSE_HEADERRESPONSE_FOOTER 的内容...但即使键入我太乐观了……我会编辑的。
  • 好吧,我的意思是您关于优化原始代码的说法,但这也是!
  • @LightnessRacesinOrbit 是的,accumulate 现在确实在内部进行复制。 P0616R0 应该在 C++20 中解决这个问题...
【解决方案3】:

只需使用stringstream 即可。 joe_chip 演示了如何直接从stringstream 复制。

std::stringstream buffer;

buffer << RESPONSE_HEADER;

for( auto param : request->paramsMap )
{
    buffer << "<tr><td>" << param.first << "</td><td>" << param.second << "</td></tr>\n";
    DEBUG_LOG_F << "<tr><td>" << param.first << "</td><td>" << param.second << "</td></tr>\n";
}

buffer << RESPONSE_FOOTER;

return sstreamToVector(buffer);

【讨论】:

  • @NishantSingh。你不认为std::string str = buffer.str(); 会减慢程序吗?如果这样做,我会再次实例化内存,只是为了从stringstream 获取数据。对我来说似乎没有必要
  • @Yura 是的。但这更容易理解
  • 只是不做字符串转换,填充stringstream后,获取streambuf实例,然后使用它的sgetn函数填充结果向量。无论如何,joe_chip 提供的解决方案似乎在做同样的事情,所以它应该同样有效
  • @Caleth,同意。但我认为用 C++ 编写我们应该专注于有效性。至少我尝试学习方法。这就是问题所在。无论如何感谢您的想法
  • @Yura:那是因为你创建了一个新缓冲区。诀窍是直接从原始副本中复制出来。乔向你展示了如何。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多