boost之lexical_cast

转自:http://www.c-view.org/person/chong/approach2boost.htm#IDAHDSAB

 

Boost简介

Boost是什么?一套开放源代码、高度可移植的C++库。
谁发起的?C++标准委员会库工作组。所以,质量保证,不怕遇到假冒伪劣产品。

有些什么呢?瞧瞧:

  • 正则表达式,可以与POSIX API和Perl语言处理正则表达式的功能相媲美,而且还能支持各种字符类型(如char、wchar_t,甚至还可以是自定义字符类型);
  • 多线程,想了很久的跨平台多线程库了;
  • 数据结构“图”,再加上即将加入标准的hash_sethash_maphash_multisethash_multimap等等(事实上不少STL实作,如SGI STL,已经支持以上数据结构),C++对数据结构的支持已近完备;
  • python,没错,对Python语言的支持;
  • 智能指针,与std::auto_ptr一起善加使用,可杜绝内存泄露,效率更不可和垃圾收集机制GC同日而语;
  • 更有循环冗余的CRC、可轻松定义返回多个值函数的元组tuple、可容纳不同类型值的any、对标准库各方面的补充……
  • 还在迅速扩大中,部分内容有望进入C++标准库……

lexical_cast

这次我们先挑个简单实用的Boost组件,看看Boost能给我们带来怎样的便利。

字符串→数值

在CSDN论坛上经常看到询问如何在字符串类型和数值类型间进行转换的问题,也看到了许多不同的答案。下面先讨论一下从字符串类型到数值类型的转换。

如何将字符串"123"转换为int类型整数123?答案是,用标准C的库函数atoi
如果要转换为long类型呢?标准C的库函数atol
如何将"123.12"转换为double类型呢?标准C的库函数atod
如果要转换为long double类型呢?标准C的库函数atold
……

后来有朋友开始使用标准库中的string类,问这个如何转换为数值?有朋友答曰,请先转换为const char*。我很佩服作答者有数学家的思维:把陌生的问题转化成熟悉的问题。(曾经有一则笑话,好事者问数学家:知道如何烧水吗?答:知道。把水壶加满水,点火烧。又问:如果水壶里已经有水了呢?答:先倒掉,就转化为我熟悉的问题了……)

不,不,这样是C的做法,不是C++。那么,C++该怎么做呢?使用Boost Conversion Library所提供的函数lexical_cast(需要引入头文件boost/lexical_cast.hpp)无疑是最简单方便的。如:

#include <boost/lexical_cast.hpp>
#include <iostream>
int main()
{
	using boost::lexical_cast;
	int a = lexical_cast<int>("123");
	double b = lexical_cast<double>("123.12");
	std::cout<<a<<std::endl
	std::cout<<b<<std::endl;
	return 0;
}
一个函数就简洁地解决了所有的问题。

数值→字符串

那么从数值类型到字符串类型呢?

itoa?不对吧,标准C/C++里根本没有这个函数。即使在Windows平台下某些编译器提供了该函数[3],没有任何移植性不说,还只能解决int类型(也许其他函数还可以解决long、unsigned long等类型),浮点类型又怎么办?当然,办法还是有,那就是:sprintf

char s[100];
sprintf(s, "%f", 123.123456);
不知道诸位对C里的 scanf/printf系列印象如何,总之阿炯我肯定记不住那些稀奇古怪的参数,而且如果写错了参数,就会得到莫名其妙的输出结果,调试起来可就要命了(我更讨厌的是字符数组,空间开100呢,又怕太小装不下;开100000呢,总觉得太浪费,心里憋气,好在C++标准为我们提供了 string这样的字符串类)。这时候, lexical_cast就出来帮忙啦。
#include <boost/lexical_cast.hpp>
#include <string>
#include <iostream>
int main()
{
	using std::string;
	const double d = 123.12;
	string s = boost::lexical_cast<string>(d);
	std::cout<<s<<std::endl;
	return 0;
}
跟前面一样简单。

异常

如果转换失败,则会有异常bad_lexical_cast抛出。该异常类是标准异常类bad_cast的子类。

#include <boost/lexical_cast.hpp>
#include <iostream>
int main()
{
	using std::cout;
	using std::endl;
	int i;
	try{
		i = boost::lexical_cast<int>("abcd");
	}
	catch(boost::bad_lexical_cast& e)
	{
		cout<<e.what()<<endl;
		return 1;
	}
	cout<<i<<endl;
	return 0;
}
显然“abcd”并不能转换为一个int类型的数值,于是抛出异常,捕捉后输出“bad lexical cast: source type value could not be interpreted as target”这样的信息。

注意事项

lexical_cast依赖于字符流std::stringstream(会自动引入头文件<sstream>[4]),其原理相当简单:把源类型读入到字符流中,再写到目标类型中,就大功告成。例如

int d = boost::lexical_cast<int>("123");
就相当于
int d;
std::stringstream s;
s<<"123";
s>>d;
既然是使用了字符流,当然就有些随之而来的问题,需要特别指出 [5]
  1. 由于Visual C++ 6的本地化(locale)部分实现有问题,因此如果使用了非默认的locale,可能会莫名其妙地抛出异常。当然,一般情况下我们并不需要去改变默认的locale,所以问题不是很大。

  2. 输入数据必须“完整”地转换,否则抛出bad_lexical_cast异常。例如

        int i = boost::lexical_cast<int>("123.123"); // this will throw
    
    便会抛出异常。因为“123.123”只能“部分”地转换为123,不能“完整”地转换为123.123。

  3. 浮点数的精度问题。

    
     std::string s = boost::lexical_cast<std::string>(123.1234567);
    
    以上语句预想的结果是得到“123.1234567”,但是实际上我们只会得到“123.123”,因为默认情况下std::stringstream的精度是6(这是C语言程序库中的“前辈”printf留下的传统)。这可以说是boost::lexical_cast的一个bug。怎么办呢?权宜之计,可以这么做:打开头文件<boost/lexical_cast.hpp>,注意对照修改[6]
    #include <boost/limits.hpp>
    //...
    template<typename Target, typename Source>
    Target lexical_cast(Source arg) {
         //...
         Target result; 
         interpreter.precision(std::numeric_limits<Source>::digits10);
         if( !(interpreter << arg) ||
         !(interpreter >> result) ||
         !(interpreter >> std::ws).eof())
     //...
    }
    即可得到正确结果。当然,理论上效率会有一点点损失,不过几乎可以忽略不计。

小结

我们已经体验了boost::lexcial_cast。当然,lexical_cast不仅仅局限于字符串类型与数值类型之间的转换:可在任意可输出到stringstream的类型和任意可从stringstream输入的类型间转换。这次的了解尽管很粗略,不过毕竟我们已经“走进Boost”,而不仅仅是“走近”。以后,我们可以自行领略Boost的动人之处啦。



[1] 如果您访问Boost英文网站出现DNS错误,不妨试试http://64.226.201.52/

[2] 请参考Boost文档中的“下载与安装说明(Boost Download and Installation)”部分。

[3] Borland C++ Builder提供了itoa,而Microsoft Visual C++提供了一个功能相同的函数,不过名字是_itoa

[4] 有些不符合标准的标准库实现中,字符流类名是strstream,在头文件<strstream>中。而标准规定的是stringstream,在头文件<sstream>中。

[6] 非常感谢Andrew Koenig和Bjarne Stroustrup两位的指教和帮助。最开始我的想法是,指定最大精度,加入interpreter.precision(15)之类的语句,然而又担心移植性的问题。Andrew Koenig先生给出了非常明确的解释:You are quite correct that 15 is not portable across all floating-point implementations. However, it is portable across all implementations that support IEEE floating-point arithmetic, which is most computers that are in common use today. If you want to do better than that, you might consider using numeric_limits<double>::digits10, which is the number of significant base-10 digits that can be accurately represented in a double.(中文大意是,诚然,15并非可移植到所有浮点实现中,但对于支持IEEE浮点运算的实现来说,则的确是可移植的,而且,这也是现今绝大部分计算机所使用的。如果想做得更好一点,则可以考虑使用numeric_limits<double>::digits10,就能表示出10进制下double能精确表达的位数。)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值