从C ++中的流中解析由一系列整数组成的文本很容易:只需解码它们即可。当以某种方式接收数据并且在程序内容易获得数据时,例如,接收base64编码的文本(解码不是问题),情况有点不同。数据位于程序内的缓冲区中,只需要解码,而不是读取。当然,可以使用std::istringstream
:
std::vector<int> parse_text(char* begin, char* end) {
std::istringstream in(std::string(begin, end));
return std::vector<int>(std::istream_iterator<int>(in),
std::istream_iterator<int>());
}
由于接收了很多这些缓冲区并且它们可能相当大,因此需要不复制字符数组的实际内容,理想情况下,还要避免为每个缓冲区创建流。因此,问题变为:
给定一个缓冲区char
s包含一系列(空格分隔;处理其他分隔符很容易完成,例如,使用合适的操纵器)整数如何解码它们而不复制序列,如果可能的话,甚至没有创建std::istream
?
答案 0 :(得分:5)
使用自定义流缓冲区可以轻松完成缓冲区的复制,只需设置get区域即可使用缓冲区。流缓冲区实际上甚至不需要覆盖任何虚函数,只需设置内部缓冲区:
class imemstream
: private virtual std::streambuf
, public std::istream
{
public:
imemstream(char* begin, char* end)
: std::streambuf()
, std::istream(static_cast<std::streambuf*>(this))
{
this->setg(begin, begin, end);
}
};
std::vector<int> parse_data_via_istream(char* begin, char* end)
{
imemstream in(begin, end);
return std::vector<int>(std::istream_iterator<int>(in),
std::istream_iterator<int>());
}
此方法可避免复制流并使用现成的std::istream
功能。但是,它确实创建了一个流对象。通过适当的更新功能,可以扩展流流/流缓冲区以重置缓冲区并处理多个缓冲区。
为避免创建流,可以使用std::num_get<...>
的基础功能。实际解析由std::locale
方面之一完成。 std::istream
的数字解析由std::num_get<char, std::istreambuf_iterator<char>>
完成。这个方面没什么帮助,因为它使用std::istreambuf_iterator<char>
指定的序列,但可以实例化std::num_get<char, char const*>
方面。它不会成为默认std::locale
的一部分,但很容易创建相应的std::locale
并安装它,例如,作为{{1}中的全局std::locale
对象}}:
main()
请注意,int main()
{
std::locale::global(std::locale(std::locale(),
new std::num_get<char, char const*>()));
...
对象将清理添加的构面,即无需添加任何清理代码:在最后std::locale
持有时,构面被引用计数并释放一个特定的方面消失了。不幸的是,为了实际使用它,需要一个std::locale
对象,它只能从某个流对象中获得。但是,可以使用任何流(尽管在多线程系统中,它应该是每个流的单独流对象,以避免意外的竞争条件):
std::ios_base
这大部分只是一些错误处理和跳过前导空格:大多数情况下,char const* skipspace(char const* it, char const* end)
{
return std::find_if(it, end,
[](unsigned char c){ return !std::isspace(c); });
}
std::vector<int> parse_data_via_istream(std::ios_base& fmt,
char const* it, char const* end)
{
std::vector<int> rc;
std::num_get<char, char const*> const& ng
= std::use_facet<std::num_get<char, char const*>>(std::locale());
std::ios_base::iostate error;
for (long tmp;
(it = ng.get(skipspace(it, end), end, fmt, error, tmp))
, error == std::ios_base::goodbit; ) {
rc.push_back(tmp);
}
return rc;
}
提供了自动跳过格式化输入的空格并处理必要的错误协议的工具。上面概述的方法可能有一个小的优点,即每个缓冲区只获取一个方面,并避免创建std::istream
对象以及避免创建流。当然,代码假定某些流可用于将其作为其std::istream::sentry
子对象传递,以提供解析标记,例如要使用的基础。
好的,对于std::ios_base&
大部分也可以做的事情来说,这是相当多的代码。使用strtol()
的方法具有一定的灵活性,std::num_get<char, char const*>
无法提供:
strtol()
方面可以被覆盖以解析任意格式的表示,例如罗马数字,因此在输入格式方面更灵活。std::locale
std::numpunct<char>
使用的std::locale
来设置它们。fmt
时将it
和it+8
作为范围来解析由8位数值组成的连续字符序列。但是,std::num_get<char, char const*>::get()
可能是大多数用途的好方法。另一方面,上面提供了一种在某些情况下可能有用的替代方案。