我想使用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"`
非常感谢任何帮助
问候
朱莉娅
答案 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 double
或qi::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;
}