我试着解析解析。
我有一些de-de
格式的数据,其中包含字符串末尾的附加信息。
我设法让de-de部分正确但我很难正确解析-
和%
。我读了codecvt
,但我不明白这个话题。
以下是我到目前为止所理解的内容的反映,以及我需要做的一个例子。
#include <string>
#include <locale>
#include <iostream>
#include <sstream>
using namespace std;
#define EXPECT_EQ(actual, expected) { \
if (actual != expected) \
{ \
cout << "expected " << #actual << " to be " << expected << " but was " << actual << endl; \
} \
}
double parse(wstring numstr)
{
double value;
wstringstream is(numstr);
is.imbue(locale("de-de"));
is >> value;
return value;
}
int main()
{
EXPECT_EQ(parse(L"123"), 123); //ok
EXPECT_EQ(parse(L"123,45"), 123.45); //ok
EXPECT_EQ(parse(L"1.000,45"), 1000.45); //ok
EXPECT_EQ(parse(L"2,390%"), 0.0239); //% sign at the end
EXPECT_EQ(parse(L"1.234,56-"), -1234.56); //- sign at the end
}
输出结果为:
expected parse(L"2,390%") to be 0.0239 but was 2.39
expected parse(L"1.234,56-") to be -1234.56 but was 1234.56
我如何灌输我的信息流,使其能够像我需要的那样读取-
和%
符号?
答案 0 :(得分:2)
codecvt
方面是错误的地方。 codecvt
facet仅用于处理将字符的外部表示转换为相同字符的内部表示(例如,文件中的UTF-8,内部的UTF-32 / UCS-4)。
要解析此类数字,您需要查找num_get
方面。基本的想法是,您将创建一个派生自std::num_get
的类,该类会覆盖do_get
(至少)您关注的数字类型。
在一个典型的情况下,你只做一个真实的&#34;几种类型的实现(例如,long long和long double),并将所有较小类型的函数委托给那些类型,然后将结果转换为目标类型。
这是一个相当简单的num_get
方面。目前,它只尝试为类型double
提供特殊处理。为了防止这个例子过于冗长,我简化了处理过程:
%-
的后缀(但会-%
)。1,,,3
将解析为13
。在这些限制中,这里有一些代码:
#include <ios>
#include <string>
#include <locale>
#include <iostream>
#include <sstream>
#include <iterator>
#include <cctype>
using namespace std;
template <class charT, class InputIterator = istreambuf_iterator<charT> >
class read_num : public std::num_get < charT > {
public:
typedef charT char_type;
typedef InputIterator iter_type;
protected:
iter_type do_get(iter_type in, iter_type end, ios_base& str, ios_base::iostate& err, double& val) const {
double ret = 0.0;
bool negative = false;
using uc = std::make_unsigned<charT>::type;
while (std::isspace((uc)*in))
++in;
if (*in == '-') {
negative = true;
++in;
while (std::isspace((uc)*in))
++in;
}
while (std::isdigit((uc)*in)) {
ret *= 10;
ret += *in - '0';
++in;
if (*in == '.')
++in;
}
if (*in == ',') {
++in;
double place = 10.0;
while (std::isdigit((uc)*in)) {
ret += (*in - '0') / place;
place *= 10;
++in;
}
}
if (*in == '-') {
negative = true;
++in;
}
if (*in == '%') {
ret /= 100.0;
++in;
}
if (negative)
ret = -ret;
val = ret;
return in;
}
};
实际上,在这种情况下你可能不希望以这种方式做事 - 你可能想委托现有的方面来读取正确的数字,然后在它解析的最后,寻找一个-
和/或%
并做出适当的反应(例如,如果您发现前导和尾随&#39; - &#39;)可能会诊断错误。
答案 1 :(得分:2)
我正在解决这个问题:让我们来解决这里的问题。
你最终还是会在某处编写,所以我忘记了首先需要创建一个(昂贵的)字符串流。
选择的武器:提升精神
注意,
我直接使用它的迭代器解析字符串。我的代码很漂亮 关于所使用的浮点数类型的通用。
您可以通过以下方式搜索替换
double
。boost::multiprecision::cpp_dec_float
(或将其设为模板 参数)并进行解析。因为我预测你需要解析器 十进制浮点数,而不是二进制浮点数。你在转换中失去了准确性。更新:扩展样本Live On Coliru
在它的核心,语法非常简单:
if (parse(numstr.begin(), numstr.end(), mynum >> matches['-'] >> matches['%'],
value, sign, pct))
{
if (sign) value = -value;
if (pct) value /= 100;
return value;
}
你有它。当然,我们需要定义mynum
,以便按预期解析无符号实数:
using namespace qi;
real_parser<double, de_numpolicy<double> > mynum;
real_policies<>
文档在解释如何tweak real number parsing using real_policies
方面有很长的路要走。这是我提出的政策:
template <typename T>
struct de_numpolicy : qi::ureal_policies<T>
{
// No exponent
template <typename It> static bool parse_exp(It&, It const&) { return false; }
template <typename It, typename Attr> static bool parse_exp_n(It&, It const&, Attr&) { return false; }
// Thousands separated numbers
template <typename It, typename Attr>
static bool parse_n(It& first, It const& last, Attr& attr)
{
qi::uint_parser<unsigned, 10, 1, 3> uint3;
qi::uint_parser<unsigned, 10, 3, 3> uint3_3;
if (parse(first, last, uint3, attr)) {
for (T n; qi::parse(first, last, '.' >> uint3_3, n);)
attr = attr * 1000 + n;
return true;
}
return false;
}
template <typename It>
static bool parse_dot(It& first, It const& last) {
if (first == last || *first != ',')
return false;
++first;
return true;
}
};
<强> Live On Coliru 强>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#define EXPECT_EQ(actual, expected) { \
double v = (actual); \
if (v != expected) \
{ \
std::cout << "expected " << #actual << " to be " << expected << " but was " << v << std::endl; \
} \
}
namespace mylib {
namespace qi = boost::spirit::qi;
template <typename T>
struct de_numpolicy : qi::ureal_policies<T>
{
// No exponent
template <typename It> static bool parse_exp(It&, It const&) { return false; }
template <typename It, typename Attr> static bool parse_exp_n(It&, It const&, Attr&) { return false; }
// Thousands separated numbers
template <typename It, typename Attr>
static bool parse_n(It& first, It const& last, Attr& attr)
{
qi::uint_parser<unsigned, 10, 1, 3> uint3;
qi::uint_parser<unsigned, 10, 3, 3> uint3_3;
if (parse(first, last, uint3, attr)) {
for (T n; qi::parse(first, last, '.' >> uint3_3, n);)
attr = attr * 1000 + n;
return true;
}
return false;
}
template <typename It>
static bool parse_dot(It& first, It const& last) {
if (first == last || *first != ',')
return false;
++first;
return true;
}
};
template<typename Char, typename CharT, typename Alloc>
double parse(std::basic_string<Char, CharT, Alloc> const& numstr)
{
using namespace qi;
real_parser<double, de_numpolicy<double> > mynum;
double value;
bool sign, pct;
if (parse(numstr.begin(), numstr.end(), mynum >> matches['-'] >> matches['%'],
value, sign, pct))
{
// std::cout << "DEBUG: " << std::boolalpha << " '" << numstr << "' -> (" << value << ", " << sign << ", " << pct << ")\n";
if (sign) value = -value;
if (pct) value /= 100;
return value;
}
assert(false); // TODO handle errors
}
} // namespace mylib
int main()
{
EXPECT_EQ(mylib::parse(std::string("123")), 123); // ok
EXPECT_EQ(mylib::parse(std::string("123,45")), 123.45); // ok
EXPECT_EQ(mylib::parse(std::string("1.000,45")), 1000.45); // ok
EXPECT_EQ(mylib::parse(std::string("2,390%")), 0.0239); // % sign at the end
EXPECT_EQ(mylib::parse(std::string("1.234,56-")), -1234.56); // - sign at the end
}
如果取消注释“DEBUG”行,则会打印:
DEBUG: '123' -> (123, false, false)
DEBUG: '123,45' -> (123.45, false, false)
DEBUG: '1.000,45' -> (1000.45, false, false)
DEBUG: '2,390%' -> (2.39, false, true)
DEBUG: '1.234,56-' -> (1234.56, true, false)