使用 boost::spirit 解析任意精度整数
Posted
技术标签:
【中文标题】使用 boost::spirit 解析任意精度整数【英文标题】:Parsing arbitrary precision integers with boost::spirit 【发布时间】:2012-09-18 16:33:23 【问题描述】:我想为任意整数创建 boost::spirit::qi::grammar。将整数存储到字符串只是感觉非常浪费内存,尤其是当整数以二进制格式表示时。如何在结构中使用任意精度整数类(例如 GMP 或 llvm::APInt)?
【问题讨论】:
【参考方案1】:如果您有一个包含一系列任意长整数的文本文件,那么 Qi 肯定可以用来非常有效地将该文件解析为单独的数字,以文本标记的形式呈现。如何将这些标记转换为 GMP 数字取决于您,但我建议库提供的通过文本输入数字的机制比您可能想出的任何东西都更优化。
如果您询问 Qi 是否可以适应读取包含任意长数字的二进制文件,那么答案是肯定的 - 已经支持二进制解析器,请参见此处:http://www.boost.org/doc/libs/1_48_0/libs/spirit/doc/html/spirit/qi/reference/binary.html。我不确定您的目标数学库整数的格式,我的猜测是您可以将这些原语链接在一起以直接读取数字的二进制表示。或者,您可以基于其中之一设计自己的解析器原语。
【讨论】:
【参考方案2】:看起来彼得在这里提出了错误的问题。
我后来回答了他自己的问题Parse arbitrary precision numbers with Boost spirit,它确实关注 LLVM 的 APInt 类型。
然而,在这个过程中,我 - 当然 - 展示了如何使用 Boost 多精度类型。
我会添加以下注释以更准确地解决这个问题的重点:
从文件读取时,使用文件映射和原始char*
而不是istream
和流迭代器。我碰巧刚刚在这里的另一个答案线程中证明了它的速度:comment link:
以下是优化阅读的方法: coliru.stacked-crooked.com/a/0dcb8a05f12a08a5。现在一切都在 ~1.28s 看起来很疯狂的优化
[...] 刮胡子的唯一方法 解析它需要几毫秒 s/double_/float_/g 但我不会 因为它消除了 Graph 模型的通用性。 – sehe 3 hours ago
通常在使用 boost 多精度类型时,您会发现必须禁用表达式模板。另外,我认为真正的任意精度类型往往不适用于int_parser<>
,但是当我尝试时,固定精度类型都可以。
如果您不需要,请考虑不解析所有数字。您可以“懒惰地”解析一些文件,只检测例如线边界或其他结构元素。然后在需要时可以详细解析感兴趣的片段。
我有一个非常详细的答案(Using boost::iostreams::mapped_file_source with std::multimap)在内存映射的文本文件上显示,您可以在没有任何内存开销的情况下对多 GB 文件进行二进制搜索,然后只解析相关区域:
Live On Coliru(包括生成测试数据)
#define NDEBUG
#undef DEBUG
#include <boost/iostreams/device/mapped_file.hpp>
#include <boost/utility/string_ref.hpp>
#include <boost/optional.hpp>
#include <boost/spirit/include/qi.hpp>
#include <thread>
#include <iomanip>
namespace io = boost::iostreams;
namespace qi = boost::spirit::qi;
template <typename Key, typename Value>
struct text_multi_lookup
text_multi_lookup(char const* begin, char const* end)
: _map_begin(begin),
_map_end(end)
private:
friend struct iterator;
enum : char nl = '\n' ;
using rawit = char const*;
rawit _map_begin, _map_end;
rawit start_of_line(rawit it) const
while (it > _map_begin) if (*--it == nl) return it+1;
assert(it == _map_begin);
return it;
rawit end_of_line(rawit it) const
while (it < _map_end) if (*it++ == nl) return it;
assert(it == _map_end);
return it;
public:
struct value_type final
rawit beg, end;
Key key;
Value value;
boost::string_ref str() const return beg, size_t(end-beg) ;
;
struct iterator : boost::iterator_facade<iterator, boost::string_ref, boost::bidirectional_traversal_tag, value_type>
iterator(text_multi_lookup const& d, rawit it) : _region(&d), _data it, nullptr, Key, Value
assert(_data.beg == _region->start_of_line(_data.beg));
private:
friend text_multi_lookup;
text_multi_lookup const* _region;
value_type mutable _data;
void ensure_parsed() const
if (!_data.end)
assert(_data.beg == _region->start_of_line(_data.beg));
auto b = _data.beg;
_data.end = _region->end_of_line(_data.beg);
if (!qi::phrase_parse(
b, _data.end,
qi::auto_ >> qi::auto_ >> qi::eoi,
qi::space,
_data.key, _data.value))
std::cerr << "Problem in: " << std::string(_data.beg, _data.end)
<< "at: " << std::setw(_data.end-_data.beg) << std::right << std::string(_data.beg,_data.end);
assert(false);
static iterator mid_point(iterator const& a, iterator const& b)
assert(a._region == b._region);
return *a._region, a._region->start_of_line(a._data.beg + (b._data.beg -a._data.beg)/2) ;
public:
value_type const& dereference() const
ensure_parsed();
return _data;
bool equal(iterator const& o) const
return (_region == o._region) && (_data.beg == o._data.beg);
void increment()
_data = _region->end_of_line(_data.beg), nullptr, Key, Value ;
assert(_data.beg == _region->start_of_line(_data.beg));
;
using const_iterator = iterator;
const_iterator begin() const return *this, _map_begin ;
const_iterator end() const return *this, _map_end ;
const_iterator cbegin() const return *this, _map_begin ;
const_iterator cend() const return *this, _map_end ;
template <typename CompatibleKey>
const_iterator lower_bound(CompatibleKey const& key) const
auto f(begin()), l(end());
while (f!=l)
auto m = iterator::mid_point(f,l);
if (m->key < key)
f = m;
++f;
else
l = m;
return f;
template <typename CompatibleKey>
const_iterator upper_bound(CompatibleKey const& key) const
return upper_bound(key, begin());
private:
template <typename CompatibleKey>
const_iterator upper_bound(CompatibleKey const& key, const_iterator f) const
auto l(end());
while (f!=l)
auto m = iterator::mid_point(f,l);
if (key < m->key)
l = m;
else
f = m;
++f;
return f;
public:
template <typename CompatibleKey>
std::pair<const_iterator, const_iterator> equal_range(CompatibleKey const& key) const
auto lb = lower_bound(key);
return lb, upper_bound(key, lb) ;
;
#include <iostream>
int main()
io::mapped_file_source map("input.txt");
text_multi_lookup<double, unsigned int> tml(map.data(), map.data() + map.size());
auto const e = tml.end();
for(auto&& line : tml)
std::cout << line.str();
auto er = tml.equal_range(line.key);
if (er.first != e) std::cout << " lower: " << er.first->str();
if (er.second != e) std::cout << " upper: " << er.second->str();
【讨论】:
我在发布自己的帖子后确实找到了这个,我应该删除我的并可能赏金这个,但这也很好。 @Peter Ah,这很有意义。赏金是很好的业力。谢谢关心!以上是关于使用 boost::spirit 解析任意精度整数的主要内容,如果未能解决你的问题,请参考以下文章
使用 boost::spirit::x3 解析成向量<boost::string_view>