use*_*573 5 c++ string-formatting boost-spirit coordinates
我需要将double值格式化为具有非常特定格式的坐标字符串,"DDMMSS.SSX"其中:
字段需要用零填充.空间不能被接受.格式化示例如下:
47.2535 ==> "471512.45N"
-0.123345 ==> "000724.04S"
Run Code Online (Sandbox Code Playgroud)
我已设法创建以下程序来完成这项工作.不过我有一些问题:
locls规则有更优雅的方式吗?它的目的是将绝对值存储到局部变量中value.是否有(希望更优雅)的方式来访问该fabs()功能?_1(_1 = _val等)的赋值是不必要的,因为我在局部变量中有值value.但是,如果我删除这些作业,我得到的只是"000000.00N".value.有更好的方法吗?我很乐意收到一些反馈意见
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/bind.hpp>
namespace karma = boost::spirit::karma;
typedef std::back_insert_iterator<std::string> iterator_type;
struct genLongitude : karma::grammar<iterator_type, double()>
{
genLongitude()
: genLongitude::base_type(start)
{
using karma::eps;
using karma::int_;
using karma::char_;
using karma::_1;
using karma::_val;
using karma::right_align;
using boost::phoenix::static_cast_;
using boost::phoenix::ref;
using boost::phoenix::if_;
start = locls
<< degrees << minutes << seconds
<< ( eps(_val < 0.0) << char_('E') | char_('W') );
locls = eps[_1 = _val, if_(_val < 0.0) [ref(value) = - _val] .else_ [ref(value) = _val]];
degrees = right_align(3,char_('0'))[int_[_1 = static_cast_<int>(ref(value))]]
<< eps[ref(value) = (ref(value) - static_cast_<int>(ref(value))) * 60 ];
minutes = right_align(2,char_('0'))[int_[_1 = static_cast_<int>(ref(value))]]
<< eps[ref(value) = (ref(value) - static_cast_<int>(ref(value))) * 60 ];
seconds = right_align(2,char_('0'))[int_[_1 = static_cast_<int>(ref(value))]]
<< char_(".")
<< eps[ref(value) = (ref(value) - static_cast_<int>(ref(value))) * 100 ]
<< right_align(2,char_('0'))[int_[_1 = static_cast_<int>(ref(value))]];
}
private:
double value;
karma::rule<iterator_type, double()> start, locls, degrees, minutes, seconds;
};
int main()
{
for(auto & value : std::vector<double>{ 47.25346, 13.984364, -0.1233453, -44.3 })
{
std::string generated;
iterator_type outiter(generated);
auto rv = karma::generate(outiter, genLatitude(), value);
std::cout << "(" << rv << ") " << value << " ==> " << generated << std::endl;
}
}
Run Code Online (Sandbox Code Playgroud)
更新:
为了完整性,这在任何示例(和答案)中修复实际上是微不足道的.纬度的格式是"DDMMSS.SSX",经度是"DDDMMSS.SSX".这是因为纬度范围是-90到+90,而经度是-180到+180.
关注点分离.
你的语法已经变得一团糟,因为你试图将所有逻辑都放在一个地方,而这并不是真正的负担.
与此同时,您已使发电机成为有状态,这意味着性能也会下降.
相反,意识到你有一个数学变换(真正的价值) - >元组(度,分,秒,半球).让我们创建一个小帮手来模拟:
struct LatLongRep {
bool _hemi; double _deg, _min, _sec;
LatLongRep(double val)
: _hemi(0 < val),
_min(60 * std::modf(std::abs(val), &_deg)),
_sec(60 * std::modf(_min, &_min))
{ }
};
Run Code Online (Sandbox Code Playgroud)
现在,你可以有这样的规则:
karma::rule<iterator_type, LatLongRep()> latitude, longitude;
Run Code Online (Sandbox Code Playgroud)
而且他们很容易实现:
latitude =
right_align(3, '0') [ uint_ ]
<< right_align(2, '0') [ uint_ ]
<< right_align(5, '0') [ seconds ]
<< east_west;
Run Code Online (Sandbox Code Playgroud)
所以整个程序变成:
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <cmath>
namespace karma = boost::spirit::karma;
typedef std::back_insert_iterator<std::string> iterator_type;
struct LatLongRep {
bool _hemi; double _deg, _min, _sec;
LatLongRep(double val)
: _hemi(0 < val),
_min(60 * std::modf(std::abs(val), &_deg)),
_sec(60 * std::modf(_min, &_min))
{ }
};
BOOST_FUSION_ADAPT_STRUCT(LatLongRep, _deg, _min, _sec, _hemi)
struct genLatLong : karma::grammar<iterator_type, double()> {
genLatLong() : genLatLong::base_type(start)
{
using namespace karma;
east_west.add (true, 'E')(false, 'W');
north_south.add(true, 'N')(false, 'S');
start = latitude;
latitude =
right_align(3, '0') [ uint_ ]
<< right_align(2, '0') [ uint_ ]
<< right_align(5, '0') [ seconds ]
<< east_west;
longitude =
right_align(3, '0') [ uint_ ]
<< right_align(2, '0') [ uint_ ]
<< right_align(5, '0') [ seconds ]
<< north_south;
}
private:
struct secfmt : karma::real_policies<double> {
unsigned precision(double) const { return 2; }
bool trailing_zeros(double) const { return true; }
};
karma::real_generator<double, secfmt> seconds;
karma::symbols<bool, char> east_west, north_south;
karma::rule<iterator_type, double()> start;
karma::rule<iterator_type, LatLongRep()> latitude, longitude;
};
int main()
{
genLatLong const gen;
for(auto & value : std::vector<double>{ 47.25346, 13.984364, -0.1233453, -44.3 })
{
std::string generated;
iterator_type outiter(generated);
auto rv = karma::generate(outiter, gen, value);
std::cout << "(" << std::boolalpha << rv << ") " << value << " ==> " << generated << std::endl;
}
}
Run Code Online (Sandbox Code Playgroud)
打印
(true) 47.2535 ==> 0471512.46E
(true) 13.9844 ==> 0135903.71E
(true) -0.123345 ==> 0000724.04W
(true) -44.3 ==> 0441760.00W
Run Code Online (Sandbox Code Playgroud)
使用派生的real_policynamed secfmt来格式化带有2个小数位的秒数; 看文档
使用融合自适应来获得不过度LatLongRep使用语义动作和/或凤凰绑定的领域(参见教程示例).另见Boost Spirit:"语义行为是邪恶的"?
用于karma::symbols<>格式化半球指标:
karma::symbols<bool, char> east_west, north_south;
east_west.add (true, 'E')(false, 'W');
north_south.add(true, 'N')(false, 'S');
Run Code Online (Sandbox Code Playgroud)发电机结构现在不在循环中 - 这大大提高了速度
使用所定义的纬度和经度作为读者的练习
再考虑一下,我来回答一下
问: 对于此类问题,一般有更好的解决方案吗?
在这种情况下,使用 Boost Format 可能会更好。重用LatLongRep- 我的其他答案中的计算主力,您可以非常轻松地创建 IO 操纵器:
namespace manip {
struct LatLongRepIO : LatLongRep {
LatLongRepIO(double val, char const* choices) : LatLongRep(val), _display(choices) { }
private:
char const* _display;
friend std::ostream& operator<<(std::ostream& os, LatLongRepIO const& llr) {
return os << boost::format("%03d%02d%05.2f%c")
% llr._deg % llr._min % llr._sec
% (llr._display[llr._hemi]);
}
};
LatLongRepIO as_latitude (double val) { return { val, "WE" }; }
LatLongRepIO as_longitude(double val) { return { val, "NS" }; }
}
Run Code Online (Sandbox Code Playgroud)
这放弃了 Boost Spirit、Phoenix 和 Fusion 的使用,使使用变得轻而易举:
int main() {
using namespace helpers::manip;
for(double value : { 47.25346, 13.984364, -0.1233453, -44.3 })
std::cout << as_latitude(value) << "\t" << as_longitude(value) << "\n";
}
Run Code Online (Sandbox Code Playgroud)
#include <boost/format.hpp>
#include <cmath>
namespace helpers {
struct LatLongRep {
bool _hemi; double _deg, _min, _sec;
LatLongRep(double val)
: _hemi(0 < val),
_min(60 * std::modf(std::abs(val), &_deg)),
_sec(60 * std::modf(_min, &_min))
{ }
};
namespace manip {
struct LatLongRepIO : LatLongRep {
LatLongRepIO(double val, char const* choices) : LatLongRep(val), _display(choices) { }
private:
char const* _display;
friend std::ostream& operator<<(std::ostream& os, LatLongRepIO const& llr) {
return os << boost::format("%03d%02d%05.2f%c")
% llr._deg % llr._min % llr._sec
% (llr._display[llr._hemi]);
}
};
LatLongRepIO as_latitude (double val) { return { val, "WE" }; }
LatLongRepIO as_longitude(double val) { return { val, "NS" }; }
}
}
#include <iostream>
int main() {
using namespace helpers::manip;
for(double value : { 47.25346, 13.984364, -0.1233453, -44.3 })
std::cout << as_latitude(value) << "\t" << as_longitude(value) << "\n";
}
Run Code Online (Sandbox Code Playgroud)
印刷
0471512.46E 0471512.46S
0135903.71E 0135903.71S
0000724.04W 0000724.04N
0441760.00W 0441760.00N
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
353 次 |
| 最近记录: |