使用Boost进行数据转换
来源:互联网 发布:中关村软件破解 编辑:程序博客网 时间:2024/05/20 01:44
以下代码来源于《深入实践Boost:Boost程序库开发的94个秘笈》一书
将字符串转换为数值
在C++中,将字符串转换为数值,效率底下,如,将字符串100转换为int类型时:
#include <sstream> // for C++ method#include <cstdlib> // for C methodint main(){#pragma region C++ std::istringstream iss("100"); int icplus; iss >> icplus; //而现在,'iss'变量会一直妨碍直到作用域的末尾#pragma endregion C++#pragma region C char* end; // long int strtol(const char *nptr,char **endptr,int base); // nptr:等待转换的字符串 // endptr:传出参数,若不为NULL,则会将遇到不合条件而终止的nptr中的字符指针返回,这里为'\0' // base:代表采用的进制方式 int ic = std::strtol("100", &end, 10);#pragma endregion C return 0;}
Boost中有一个库Boost.LexicalCast,可用于解决字符串向数值转换的困难,它由一个boost::bad_lexical_cast异常类和少数boost::lexical_cast函数组成:
#include <boost/lexical_cast.hpp>int i = boost::lexical_cast<int>("100");//甚至可以用于非零结尾的字符串:char chars[] = {'1','0','0'};int i = boost::lexical_cast<int>(chars,3);assert(i == 100);
工作原理
boost::lexical_cast函数接受字符串作为输入,并将其转换成尖括号中指定的类型。
boost::lexical_cast函数甚至还会检查边界:
try { // 在x86钟,short通常不能存储大于32767的值 short s = boost::lexical_cast<short>("1000000"); assert(false); // 不能到达这里 } catch (const boost::bad_lexical_cast&) { std::cout << "catch a bad_lexical_cast here" << std::endl; }
并且还能检查输入的语法是否正确:
try { int i = boost::lexical_cast<int>("This is not a number"); assert(false); //不能到达这里 (void)i; //抑制有关未使用的变量的警告 } catch (const boost::bad_lexical_cast&) { std::cout << "catch a bad_lexical_cast here" << std::endl; }
词汇强制转换(lexical_cast)就像所有使用std::locale的std::stringstreams类一样,它也使用std::locale,并且能对本地化数值做转换,还有一系列引人注目的优化,专门针对C locale(语言环境)以及对数值不进行分节的语言环境:
#include <locale> std::locale::global(std::locale("ru_RU.UTF8")); // 在俄语中用逗号作为小数点(分隔符) float f = boost::lexical_cast<float>("1,0"); assert(f < 1.01 && f > 0.99);
boost::lexical_cast可以用于创建将其他类型转换为数值的模板函数。
// 将包含一些string值的容器转换为long int值的向量#include <iostream>#include <algorithm>#include <vector>#include <set>#include <deque>#include <iterator>#include <boost/lexical_cast.hpp>template <class ContainerT>std::vector<long int> container_to_longs(const ContainerT& container){ typedef typename ContainerT::value_type value_type; std::vector<long int> ret; typedef long int(*func_t)(const value_type&); func_t f = &boost::lexical_cast<long int, value_type>; std::transform(container.begin(), container.end(), std::back_inserter(ret), f); return ret;}int main(){ std::set<std::string> str_set; str_set.insert("1"); assert(container_to_longs(str_set).front() == 1); std::deque<const char*> char_deque; char_deque.push_front("1"); char_deque.push_back("2"); assert(container_to_longs(char_deque).front() == 1); assert(container_to_longs(char_deque).back() == 2); typedef boost::array<unsigned char, 2> element_t; boost::array<element_t, 2> arrays = { { {{'1','0'}},{{'2','0'}}} }; assert(container_to_longs(arrays).front() = 10); assert(container_to_longs(arrays).back() == 20); system("pause"); return 0;}
将数值转换为字符串
1.使用boost::lexical_cast将整数100转换为std::string:
#include <boost/lexical_cast.hpp> std::string s = boost::lexical_cast<std::string>(100); assert(s == "100");
2.此法与传统的C++的转换方法相比较:
#include <sstream> // C++转换喂字符串的方式 std::stringstream ss; ss << 100; std::string s; ss >> s; // 变量'ss'直到作用域的末尾都是多余的 // 多个虚拟方法再转换期间被调用 assert(s == "100");
与C的转换方法对比:
#include <cstdlib> // C转换为字符串的方式 char buffer[100]; std::sprintf(buffer,"%i",100); // 像对所有函数一样,你将需要一个unsigned long long int 类型 // 来对'printf'中出现了多少个错误进行计算 // 'printf'函数是固有的安全威胁! std::string s(buffer); // 现在,不再使用buffer变量 assert(s == "100");
将数值转换为数值
通常情况下,我们会有这样的代码:
void some_function(unsigned short param);int foo(); some_function(foo()); // 将其显式转换喂无符号短整数的数据类型来忽略这些警告 some_function(static_cast<unsigned short>(foo()));
这样的会使得它的错误非常难以检测
Boost.NumericConversion库提供了解决方案,只需将static_cast替换为boost::numeric_cast。当源值不能在目标中存储时,它会抛出一个异常。
#include <boost/numeric/conversion/cast.hpp>void correct_implementation(){ some_function(boost::numeric_cast<unsigned short>(foo()));}void test_function(){ for (unsigned int i = 0; i < 100; ++i) { try { correct_implementation(); } catch (const boost::numeric::bad_numeric_cast& e) { std::cout << '#' << i << ' ' << e.what() << std::endl; } }}// 甚至可以检测到特定的溢出类型:void test_function1(){ for (unsigned int i = 0; i < 100; ++i) { try { correct_implementation(); } catch (const boost::numeric::positive_overflow& e) { // 正的溢出 std::cout << "POS OVERFLOW in #" << i << ' ' << e.what() << std::endl; } catch (const boost::numeric::negative_overflow& e) { // 负的溢出 std::cout << "NEG OVERFLOW in #" << i << ' ' << e.what() << std::endl; } }}
Boost.NumericConversion库有一个非常快的实现,它可以在编译时做很多工作。例如,当转换喂范围更宽的类型时,源代码将只调用static_cast方法。还有更多boost::numeric_cast函数是通过boost::numeric::converter实现的,它可以被调整为使用不同的溢出、范围检查和四舍五入策略。
下面例子演示了如何为boost::numeric::cast制作自己的mythrow_overflow_handler处理程序:
#include <iostream>#include <boost/numeric/conversion/cast.hpp>template <class SourceT,class TargetT>struct mythrow_overflow_handler{ void operator()(boost::numeric::range_check_result t) { if (r != boost::numeric::cInRange) { throw std::logic_error("Not in range!"); } }};template <class TargetT,class SourceT>TargetT my_numeric_cast(const SourceT& in){ using namespace boost; typedef numeric::conversion_traits<TargetT, SourceT> conv_traits; typedef numeric::numeric_cast_traits<TargetT, SourceT> cast_traits; typedef boost::numeric::converter < TargetT, SourceT, conv_traits, mythrow_overflow_handler<SourceT, TargetT> > converter; return converter::convert(in);}int main(){ try { my_numeric_cast<short>(10000); } catch (const std::logic_error& e) { std::cout << "It works!" << e.what() << std::endl; } system("pause"); return 0;}
用户定义类型与字符串的相互转换
Boost.LexicalCast还有一个功能就是允许用户在lexical_cast中使用他们自己的类型。此功能只需用户为他们的类型编写正确的std::ostream和std::istream操作符。
#include <iostream>#include <iosfwd>#include <stdexcept>#include <boost/lexical_cast.hpp>class negative_number{ unsigned short number_;public: explicit negative_number(unsigned short number) :number_(number) {} unsigned short value_without_sign() const { return number_; }};std::ostream& operator<<(std::ostream& os, const negative_number& num){ os << "-" << num.value_without_sign(); return os;}std::istream& operator >> (std::istream& is, negative_number& num){ char ch; is >> ch; if (ch != '-') { throw std::logic_error("negative_number class designed to store ONLY negative values"); } unsigned short s; is >> s; num = negative_number(s); return is;}int main(){ negative_number n = boost::lexical_cast<negative_number>("-100"); std::cout << n.value_without_sign() << std::endl; int i = boost::lexical_cast<int>(n); std::cout << i << std::endl; typedef boost::array<char, 10> arr_t; arr_t arr = boost::lexical_cast<arr_t>(n); assert(arr[0] == '-'); assert(arr[1] == '1'); assert(arr[2] == '0'); assert(arr[3] == '0'); assert(arr[4] == '\0'); system("pause"); return 0;}
强制转换多态对象
一个非常可怕的接口:
struct object{ virtual ~object(){}};struct banana:public object{ void eat() const{} virtual ~banana(){}};struct pidgin:public object{ void fly() const{} virtual ~pidgin(){}};object* try_produce_banana();
写一个吃香蕉的函数,如果传过来的东西不是香蕉,抛出异常。如果对try_produce_banana()函数返回的一个值解引用,那么就有对一个空指针解引用的危险。
可以这样做:
void try_eat_banana_impl1(){ const object* obj = try_produce_banana(); if(!obj) { throw std::bad_cast(); } // dynamic_cast将一个基类对象指针(或引用)cast到继承类指针, // dynamic_cast会根据基类指针是否真正指向继承类指针来做相应处理 dynamic_cast<const banana&>(*obj).eat();}
使用Boost.Conversion的解决方案:
#include <boost/cast.hpp>void try_eat_banana_impl2(){ const object* obj = try_produce_banana(); boost::polymorphic_cast<const banana*>(obj)->eat(); // polymorphic_cast函数包装了第一个实现的代码, // 它检查输入是否为空,然后试图做一个动态转换。 // 在这些操作中的任何错误都将抛出一个std::bad_cast异常。}
解析简单的输入
从一个简单的任务开始,解析如下一个ISO格式的日期:YYYY-MM-DD
以下是可能的输入例子:
2013-03-01
2012-12-31
从地址 http://www.ietf.org/rfc/rfc3339.txt 查看解析器的语法:
date-fullyear = 4DIGIT
date-month = 2DIGIT ; 01-12
date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year
full-date = date-fullyear “-” date-month “-” date-mday
Boost.Spirit库
它允许直接用C++代码的格式编写解析器(以及词法分析器和代码生成器),它是可以立即执行的(也就是说不需要额外的C++代码生成工具)
Boost.Spirit的语法与扩展巴克斯范式(Extended Backus-Naur Form,EBNF)非常接近,很多标准都用它来表达语法,并且其他流行的解析器都理解它。
使用时需要包括的头文件:
#include <boost/spirit/include/qi.hpp>#include <boost/spirit/include/phoenix_core.hpp>#include <boost/spirit/include/phoenix_operator.hpp>#include <assert.h>
制作一个date结构来保存解析出的数据:
struct date{ unsigned short year; unsigned short month; unsigned short day;};
解析器编写:
date parse_date_time1(const std::string& s){ using boost::spirit::qi::_1; using boost::spirit::qi::ushort_; using boost::spirit::qi::char_; using boost::phoenix::ref; date res; const char* first = s.data(); const char* const end = first + s.size(); bool success = boost::spirit::qi::parse(first, end, ushort_[ref(res.year) = _1] >> char('-') >> ushort_[ref(res.month) = _1] >> char('-') >> ushort_[ref(res.day) = _1]); if (!success || first != end) { throw std::logic_error("Parsing failed"); } return res;}
使用解析器:
int main(){ date d = parse_date_time1("2012-12-31"); std::cout << "year:" << d.year << " month:" << d.month << " day:" << d.day << std::endl; // d = parse_data_time1("2012-132-652");也能解析成功 system("pause"); return 0;}
修改以上的解析器,以便可以处理数字的计数,采用unit_parser模板类,并设置正确的参数。
date parse_date_time2(const std::string& s){ using boost::spirit::qi::_1; using boost::spirit::qi::uint_parser; using boost::spirit::qi::char_; using boost::phoenix::ref; // 使用unsigned short 作为输出类型,需要十进制,并用2位到2位的数字 uint_parser<unsigned short, 10, 2, 2> u2_; // 使用unsigned short 作为输出类型,需要十进制,并用4位到4位的数字 uint_parser<unsigned short, 10, 4, 4> u4_; date res; const char* first = s.data(); const char* const end = first + s.size(); bool success = boost::spirit::qi::parse(first, end, u4_[ref(res.year) = _1] >> char('-') >> u2_[ref(res.month) = _1] >> char('-') >> u2_[ref(res.day) = _1]); if (!success || first != end) { throw std::logic_error("Parsing failed"); } return res;}int main(){ try { date d = parse_date_time2("2012-122-31"); std::cout << "year:" << d.year << " month:" << d.month << " day:" << d.day << std::endl; } catch (const std::logic_error& e) { std::cout << e.what() << std::endl; } system("pause"); return 0;}
解析输入
前面是一个简单的解析日期的输入,也许STL手工实现解析更简单,但现在,假设需求改变,需要支持多种输入格式并加上时区偏移的日期和时间的解析器,如,应该能支持以下输入:
2012-10-20T10:00:00Z // 带有零时区偏移的日期和时间
2012-10-20T10:00:00 // 未指定时区偏移的日期和时间
2012-10-20T10:00:00+09:15 // 带有时区偏移的日期和时间
2012-10-20-09:15 // 带有时区偏移的日期和时间
10:00:09+09:15 // 带有时区偏移的时间
开始:
编写一个保存解析结果的“日期-时间”结构体
struct datetime{ enum zone_offsets_t { OFFSET_NOT_SET, OFFSET_Z, OFFSET_UTC_PLUS, OFFSET_UTC_MINUS };private: unsigned short year; unsigned short month; unsigned short day; unsigned short hours; unsigned short minutes; unsigned short seconds; zone_offsets_t zone_offset_type; unsigned int zone_offset_in_min; static void dt_assert(bool v, const char* msg) { if (!v) { throw std::logic_error("Assertion failed:" + std::string(msg)); } }public: datetime():year(0),month(0),day(0) ,hours(0),minutes(0),seconds(0) ,zone_offset_type(OFFSET_NOT_SET),zone_offset_in_min(0) {} // 获取器 // ... // 设置器 void set_year(unsigned short y) { year = y; } void set_month(unsigned short m) { month = m; } void set_day(unsigned short d) { day = d; } void set_hours(unsigned short h) { hours = h; } void set_minutes(unsigned short min) { minutes = min; } void set_seconds(unsigned short s) { seconds = s; } void set_zone_offset_type(zone_offsets_t zonetype) { zone_offset_type = zonetype; } void set_zone_offset_in_min(unsigned int min) { zone_offset_in_min = min; }};
编写解析器:
// 使用Boost.Spirit中的bind()函数,因为它能更好地遍历解析器#include <boost/spirit/include/phoenix_bind.hpp>datetime parse_datetime(const std::string& s){ using boost::spirit::qi::_1; using boost::spirit::qi::_2; using boost::spirit::qi::_3; using boost::spirit::qi::uint_parser; using boost::spirit::qi::char_; using boost::phoenix::bind; using boost::phoenix::ref; datetime ret; uint_parser<unsigned short, 10, 2, 2> u2_; uint_parser<unsigned short, 10, 4, 4> u4_; // 时区偏移解析器 boost::spirit::qi::rule<const char*, void()> timezone_parser = -( // 一元减号表示可选规则 // 零偏移 char_('Z')[bind(&datetime::set_zone_offset_type, &ret, datetime::OFFSET_Z)] | // 或指定时区偏移量 ((char_('+') | char_('-')) >> u2_ >> ':' >> u2_)[bind(&set_zone_offset, ref(ret), _1, _2, _3)] ); // 日期解析器 boost::spirit::qi::rule<const char*, void()> date_parser = u4_[bind(&datetime::set_year, &ret, _1)] >> char_('-') >> u2_[bind(&datetime::set_month, &ret, _1)] >> char_('-') >> u2_[bind(&datetime::set_day, &ret, _1)]; // 时间解析器 boost::spirit::qi::rule<const char*, void()> time_parser = u2_[bind(&datetime::set_hours, &ret, _1)] >> char_(':') >> u2_[bind(&datetime::set_minutes, &ret, _1)] >> char_(':') >> u2_[bind(&datetime::set_seconds, &ret, _1)]; const char* first = s.data(); const char* const end = first + s.size(); bool success = boost::spirit::qi::parse(first, end, ((date_parser >> char_('T') >> time_parser) | date_parser | time_parser) >> timezone_parser );}
- 使用Boost进行数据转换
- 使用@InitBinder进行数据转换
- (007):使用LINQ进行数据转换(C#)
- LINQ(三)使用 LINQ 进行数据转换
- python使用pandas进行数据转换
- SpringMVC使用ConversionService进行数据转换
- 使用 boost 进行 CRC32 校验
- boost polymorphic_cast 进行多态转换
- 用boost::lexical_cast进行数值转换
- 用boost::lexical_cast进行数值转换
- 使用MySql ODBC进行MYsql和MSsql的数据转换
- 使用mysql odbc进行mysql和mssql的数据转换
- 使用MySql ODBC进行MYsql和MSsql的数据转换
- WPF 使用值转换器进行绑定数据的转换IValueConverter
- 如何使用WPF图表控件Chart FX进行数据转换
- GoldenGate中使用strcat和strext进行数据转换
- javascript中使用枚举定义一个对象进行数据转换
- boost学习笔记1 数据转换
- https原理:证书传递、验证和数据加密、解密过程解析
- [unity3D基础篇01]空间坐标系和物体移动的较深入分析
- 关于刷新ifreme 当前页的问题
- HTTP要点概述:七,编码,压缩传输,分块传输
- javaSE学习05_基本类型的类型转换
- 使用Boost进行数据转换
- 字符串匹配的KMP算法
- perf stat 输出解读
- 基于camera+SurfaceView+MediaRecorder的录制微视频
- Html5应用开发
- React-Native 开发(二) 在react-native 中 运用 redux
- 1103. Integer Factorization (30)
- Asp中的MSSQL数据库访问:driver={SQL Server};与Provider = Sqloledb;的区别
- SAML Web SSO学习