提升标记符但保持分隔符

时间:2015-04-23 19:22:16

标签: c++ boost tokenize

也许这很容易,但我自己找不到答案。

我想使用boost :: tokenizer但是使用字符串

保留分隔符

我的字符串是一堆像这样的数字

"1.00299 344.2221-25.112-33112"

结果应该是:

"1.00299"  "344.2221"  "-25.112" "-33112"

我知道它看起来有点奇怪,但文件是这样写的。

另一个问题有点复杂,因为有些字符串是这样的:

"1.00299E+45 344.22E-21-25.112E+11-3.31E-12" 

应该是:

"1.00299E+45" "344.22E-21" "-25.112E+11"  "-3.31E-12"`

非常感谢任何帮助

问候

朱莉娅

4 个答案:

答案 0 :(得分:2)

让我们实现一个允许你做的requote操纵器:

#include "requote.hpp"
#include <iostream>

int main() {
    std::cout << requote(std::cin);
}

现在requote.hpp中有什么?

#include <istream>

struct requote {
    requote(std::istream& is) : _is(is.rdbuf()) {}

    friend std::ostream& operator<<(std::ostream& os, requote const& manip) {
        return manip.call(os);
    }

  private:
    std::ostream& call(std::ostream& os) const;
    mutable std::istream _is;
};
  

注意:我们使用相同的streambuf实例化私有istream,因此流状态是隔离的。

所有魔法都在call()。以下是我使用Boost Spirit的方法。 copy_out的复杂性是确保

  • 除引号外,我们不会更改输入演示文稿的任何部分<精简,格式化
  • 它尽可能高效(我们不构造任何临时字符串,解析异常的例外)
#include "requote.hpp"

namespace /*anon*/ {
    struct copy_out {
        mutable std::ostreambuf_iterator<char> out;

        //template <typename...> struct result { typedef void type; };
        template <typename R> void operator()(R const& r) const {
            *out++ = '"';
            out = std::copy(r.begin(), r.end(), out);
            *out++ = '"';
            *out++ = ' ';
        }
    };
}

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

std::ostream& requote::call(std::ostream& os) const {
    boost::phoenix::function<copy_out> copy_out_({os});
    using namespace boost::spirit::qi;

    boost::spirit::istream_iterator f(_is >> std::noskipws), l;
    bool ok = phrase_parse(f,l,
            *('"' > *raw[long_double][copy_out_(_1)] > '"') [boost::phoenix::ref(os)<<'\n'],
            space
        );

    if (ok && f==l)
        return os;

    throw std::runtime_error("parse error at '" + std::string(f,l) + "'");
}

样本

<强> Self-Contained On Coliru

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

struct requote {
    requote(std::istream& is) : _is(is.rdbuf()) {}

    friend std::ostream& operator<<(std::ostream& os, requote const& manip) {
        return manip.call(os);
    }

  private:
    std::ostream& call(std::ostream& os) const {
        boost::phoenix::function<copy_out> copy_out_({os});
        using namespace boost::spirit::qi;

        boost::spirit::istream_iterator f(_is >> std::noskipws), l;
        bool ok = phrase_parse(f,l,
                *('"' > *raw[long_double][copy_out_(_1)] > '"') [boost::phoenix::ref(os)<<'\n'],
                space
            );

        if (ok && f==l)
            return os;

        throw std::runtime_error("parse error at '" + std::string(f,l) + "'");
    }

    struct copy_out {
        mutable std::ostreambuf_iterator<char> out;

        //template <typename...> struct result { typedef void type; };
        template <typename R> void operator()(R const& r) const {
            *out++ = '"';
            out = std::copy(r.begin(), r.end(), out);
            *out++ = '"';
            *out++ = ' ';
        }
    };
    mutable std::istream _is;
};

#include <iostream>
int main() {
    std::cout << requote(std::cin);
}

问题中样本的输出:

"1.00299" "344.2221" "-25.112" "-33112" 
"1.00299E+45" "344.22E-21" "-25.112E+11" "-3.31E-12" 

答案 1 :(得分:2)

有人提请我注意你可能实际上并不想要那里的引号。

如果您只是想以完全保真的方式解析数字¹:

<强> Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_match.hpp>
#include <iostream>
namespace qi = boost::spirit::qi;

int main() {
    std::vector<double> values;
    std::cin >> std::noskipws >> qi::phrase_match(*('"'>*qi::double_>'"'), qi::space, values);

    for (auto d : values)
        std::cout << d << "\n";
}

打印哪些:

1.00299
344.222
-25.112
-33112
1.00299e+45
3.4422e-19
-2.5112e+12
-3.31e-12

¹您可以使用long doubleqi::real_parser<T>选择任意精度/十进制数类型;见例如Boost::Lexical_cast conversion to float changes data

答案 2 :(得分:0)

通常,标记生成器会丢弃标记,而您指定的问题看起来比标记化程序可以处理的要复杂一些。你有时希望拆分+或 - ,但前提是它不是在'E'之后。这不是一个逻辑,你可以很容易地向一个通用的标记器解释。

你应该考虑编写一个方法来自己解析字符串。您甚至可以将标记生成器拆分为'',然后解析子字符串以处理其他情况。

如果您可以控制输入数据,最好在值之间强制使用空格,然后拆分为''。

答案 3 :(得分:0)

假设您需要将这些值作为双精度读取,您可以使用std::strtod在3行中执行此操作:

#include <cstdlib>
#include <vector>

std::vector<double> parse(const char * p)
{
    std::vector<double> d;

    while ( *p )
        d.push_back( std::strtod(p, const_cast<char**>(&p)) );

    return d;
}

或者使用标准流:

std::vector<double> parse(std::istream & in)
{
    /** Assuming default flags for *in* */
    std::vector<double> d;

    for (double v; in >> v; )
        d.push_back(v);
    return d;
}