因此
istringstream
在初始化时复制字符串的内容,例如
string moo("one two three four");
istringstream iss(moo.c_str());
我想知道是否有办法让
std::istringstream
使用给定的 c_str
作为缓冲区而不复制内容。这样,在将 std::istringstream&
传递给以 istream&
作为参数的函数之前,就不必复制大量内存。
我一直在尝试做的是将一些只接受
std::ifstream&
参数的函数(它们主要是解析器)转换为也接受 istream&
。我是否必须为此创建自己的istream
子类?
使用
istringstream
并不是一个令人满意的解决方案,因为这会复制整个缓冲区。
之前的答案建议已弃用的
istrstream
,但由于这会产生警告并且将来可能会被删除,更好的解决方案是使用 boost::iostreams:
boost::iostreams::stream<boost::iostreams::array_source> stream(moo.c_str(), moo.size());
这避免了以与
istrstream
相同的方式复制缓冲区,并且使您不必编写自己的流类。
编写一个从给定内存区域读取的基本
std::streambuf
类相当简单。然后您可以从中构造一个 istream
并从中读取。
从内存缓冲区初始化 C++ std::istringstream?
请注意,指向
c_str()
的缓冲区的生命周期非常有限,并且不能保证对 c_str()
的调用会导致一些复制,尽管我不知道它的任何实现。
已弃用的
istrstream
支持此功能。
#include <string>
#include <strstream>
using namespace std;
int main(int argc, char* argv[])
{
string moo = "one two three four";
istrstream istr(const_cast<char*>(moo.c_str()),moo.size());
std::string line;
while(!istr.fail() && !istr.eof()){
getline(istr,line,' ');
cout << line << "_";
}
// prints: one_two_three_four_
}
只有一个副本,因为您传递的参数
const char*
需要转换为istringstream构造函数的参数类型。
只需传入
string
,无需调用c_str()
。
istringstream iss(moo);
好吧,这并不能完全阻止复制,但它消除了不必要的复制。要完全消除副本,您必须重写
std::stringbuf
,这特别避免直接在您提供的 string
上工作。
这取决于 std::string 的作用。根据 27.2.1/1
The class basic_istringstream<charT,traits,Allocator> ... uses a basic_stringbuf<charT,traits,Allocator> object to control the associated storage.
由于类必须使用对象,因此必须将字符串复制构造到该对象中。
因此,真正的问题不是
stringstream
是否复制内容,而是复制构造字符串是否会复制内容或实现某种写时复制方案。
经过几天的搜索和尝试各种方法包括scnlib、sscanf、istringstream等,也许我找到了答案。如果您关心解析性能,则应该使用 C++17 中添加的
charsconv header中的
from_chars
系列函数。这是在 C++ 中快速解析 string_view
的唯一合理解决方案。
根据cppreference:
与 C++ 和 C 库中的其他解析函数不同,std::from_chars 与语言环境无关、不分配、不抛出。仅提供其他库(例如 std::sscanf)使用的一小部分解析策略。这样做的目的是允许尽可能快的实现,这在常见的高吞吐量上下文中非常有用,例如基于文本的交换(JSON 或 XML)。
其他方法存在的问题如下:
scnlib:我发现它的性能与传入字符串的长度有关,即使您只解析整数,传入长字符串也会导致线性性能下降。这对于需要高性能的场景来说是不可接受的。
istringstream: 正如问题所说,它无法处理作用域,并且不可避免地需要在初始化时进行副本。
sscanf: C 风格的空终止字符串是必要的,而根据 cppreference,某些实现同样可能会导致性能问题。