gpt4 book ai didi

c++ - 如何解析存储在文本缓冲区中的整数序列?

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:34:37 25 4
gpt4 key购买 nike

在 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 缓冲区,其中包含一系列(空格分隔;处理其他分隔符很容易完成,例如,使用合适的操纵器)整数如何在不复制序列的情况下解码它们,并且,如果可能,甚至不创建 std::istream?

最佳答案

使用自定义流缓冲区可以轻松避免缓冲区的拷贝,该缓冲区只需设置获取区域即可使用缓冲区。流缓冲区实际上甚至不需要覆盖任何虚函数,只需设置内部缓冲区即可:

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*> facet可以被实例化。它不会属于默认 std::locale 的一部分但很容易创建相应的 std::locale并安装它,例如,作为全局 std::locale main() 中的第一件事:

int main()
{
std::locale::global(std::locale(std::locale(),
new std::num_get<char, char const*>()));
...

请注意 std::locale object 将清理添加的 facet,即不需要添加任何清理代码:facet 被引用计数并在最后一个 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提供自动跳过格式化输入空白的工具,并处理必要的错误协议(protocol)。上面概述的方法在每个缓冲区仅获取一次分面并避免创建 std::istream::sentry 方面可能有一个小优势。对象以及避免创建流。当然,代码假设可以使用一些流将其作为 std::ios_base& 传递进来。子对象提供解析标志,例如要使用的基数。

好的,这是相当多的代码,用于 strtol() 的东西大多数情况下也可以。使用 std::num_get<char, char const*> 的方法具有 strtol() 不提供的一些灵 active :

  1. std::locale的 facet 可以被覆盖以解析任意表示格式,例如罗马数字,它在输入格式方面更加灵活。
  2. 设置千位分隔符的使用或更改小数点的表示很容易(只需更改 std::numpunct<char> 使用的 std::locale 中的 fmt 即可设置这些)。
  3. 缓冲区不必以 null 结尾。例如,可以通过输入 it 来解析由 8 个数字值组成的连续字符序列。和 it+8作为调用 std::num_get<char, char const*>::get() 时的范围.

然而,strtol()对于大多数用途来说,这可能是一种好方法。另一方面,上述内容提供了一种可能在某些情况下有用的替代方法。

关于c++ - 如何解析存储在文本缓冲区中的整数序列?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23036783/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com