如何解析存储在文本缓冲区中的整数序列
How to parse a sequence of integers stored in a text buffer?
在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
?
使用自定义流缓冲区可以很容易地避免缓冲区的副本,该缓冲区只需设置使用缓冲区的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>
s指定的序列,但可以实例化std::num_get<char, char const*>
方面。它不会是默认std::locale
的一部分,但很容易创建相应的std::locale
并将其安装,例如,作为main()
中的全局std::locale
对象:
int main()
{
std::locale::global(std::locale(std::locale(),
new std::num_get<char, char const*>()));
...
请注意,std::locale
对象将清理添加的facet,即不需要添加任何清理代码:facet被引用计数,并在持有特定facet的最后一个std::locale
消失时释放。不幸的是,要真正使用facet,它需要一个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
的方面,它可以被覆盖来解析任意格式的表示,例如罗马数字,因此它在输入格式方面更灵活 - 设置使用数千个分隔符或更改小数点的表示方式很容易(只需更改
fmt
使用的std::locale
中的std::numpunct<char>
即可设置这些分隔符) - 缓冲区不必以null结尾。例如,当调用
std::num_get<char, char const*>::get()
时,可以通过输入it
和it+8
作为范围来解析由8个数字值组成的连续字符序列
然而,strtol()
对于大多数用途来说可能是一个不错的方法。另一方面,上述内容提供了在某些情况下可能有用的替代方案。
- 如何反转整数参数包
- enum是C++中的宏变量还是整数变量
- C++字符*缓冲区的大小
- 努力将整数转换为链表。不知道我在这里做错了什么
- 整数不会重复超过随机数
- 在C++中手动调整数组大小
- 检查输入是否不是整数或数字
- C++使用整数的压缩数组初始化对象
- 如何从uint8_t的缓冲区读取带符号整数,而不调用未定义或实现定义的行为
- 将ITOA与整数一起使用.设置缓冲区的限制
- C :带有整数指数作为迭代器的圆形缓冲区
- 如何在 c++ 中从整数缓冲区中提取 4 个字节的整数
- C++协议缓冲区,发送整数数组
- 将缓冲区的地址存储为无符号整数;
- 如何从无符号字符(8位)缓冲区读取短整数(16位)
- 写入wav[PMC]的音频缓冲区整数会产生高噪声
- 无法将整数写入缓冲区(char*)内的偏移量
- 将整数转换为字符缓冲区以进行串行通信
- 正在将字符缓冲区转换为整数(arduino)
- 如何解析存储在文本缓冲区中的整数序列