由boost::lexical_cast引发的一个问题

2008-11-27 09:28:32 来源:不详 作者:佚名 点击:
中午同事碰见一个关于使用boost::lexical_cast产生异常的问题,关键代码如下



中午同事碰见一个关于使用boost::lexical_cast产生异常的问题,关键代码如下

string str(8,'\0');
strncpy(&str.at(0),"1234567",7);
cout << lexical_cast<int>(str) << endl;

结果运行的时候发生如下异常

terminate called after throwing an instance of 'boost::bad_lexical_cast'
what(): bad lexical cast: source type value could not be interpreted as target

我们知道boost::lexical_cast最终使用的stringstream实现的数值类型转换,所以,我们使用如下例子,做测试

stringstream ss;
ss << str;
ss >> result;
cout << "new Result: " << result << endl;

编译运行后,输出

new Result: 1234567

可以正常显示,好像没有问题,

我们察看一下boost的源代码

vim /usr/include/boost/lexical_cast.hpp

察看lexical_cast函数

template<typename Target, typename Source>
Target lexical_cast(Source arg)
{
detail::lexical_stream<Target, Source> interpreter;
Target result;

if(!(interpreter << arg && interpreter >> result))
throw_exception(bad_lexical_cast(typeid(Target), typeid(Source)));
return result;
}

可见lexical_cast函数非常简单,就是具体执行operator<<和operator>>两个操作,只要这两个操作有一个失败就抛出一个异常,为了确认是那步出的错,我们在程序中手工执行这两个操作。代码如下

detail::lexical_stream<int, string> interpreter;
int result;

if(!(interpreter << str ))
{
cout << "Error 1" << endl;
}
if(!(interpreter >> result))
{
cout << "Error 2" << endl;
}
cout << result << endl;

编译运行后输出

Error 2

从这里我们知道,lexical_cast是在执行输出流的时候发生的问题,察看detail的operator>>函数,其源代码如下

template<typename InputStreamable>
bool operator>>(InputStreamable &output)
{
return !is_pointer<InputStreamable>::value &&
stream >> output &&
(stream >> std::ws).eof();
}

根据以上代码和我们使用stringstream做的测试,基本上可以确定在stream>>output(包括次步)都是正确的,可能出现问题的是(stream >> std::ws).eof();

这里解释下std::ws和stringstring::eof()函数

Std::ws函数声明在

/usr/include/c++/3.4.4/bits/istream.tcc

源代码如下

// 27.6.1.4 Standard basic_istream manipulators
template<typename _CharT, typename _Traits>
basic_istream<_CharT,_Traits>&
ws(basic_istream<_CharT,_Traits>& __in)
{
typedef basic_istream<_CharT, _Traits> __istream_type;
typedef typename __istream_type::__streambuf_type __streambuf_type;
typedef typename __istream_type::__ctype_type __ctype_type;
typedef typename __istream_type::int_type __int_type;

const __ctype_type& __ct = use_facet<__ctype_type>(__in.getloc());
const __int_type __eof = _Traits::eof();
__streambuf_type* __sb = __in.rdbuf();
__int_type __c = __sb->sgetc();

while (!_Traits::eq_int_type(__c, __eof)
&& __ct.is(ctype_base::space, _Traits::to_char_type(__c)))
__c = __sb->snextc();

if (_Traits::eq_int_type(__c, __eof))
__in.setstate(ios_base::eofbit);
return __in;
}

主要作用是过滤输入流中的空格,\n\r等字符。stream >> std::ws目的就是把输入流中转换完整形后的剩余流内容(假如有的话)写入std::ws,当然只能写入其中的空格和\n\r等字符。

stringstring::eof()函数参考 http://www.cppreference.com/wiki/io/eof 部分

该函数的主要作用是,如果到达流的结束位置返回true,否则返回false

根据以上信息,我们编写测试用例

stringstream ss;
ss << str;
ss >> result;
cout << "new Result: " << result << endl;

cout << ss.eof() << endl;
cout << (ss >> std::ws).eof() << endl;

编译运行后输出

new Result: 1234567

0

0

由此可见,虽然我们使用ss时,可以输出想要的正确结果,但是我们缺少最后的安全验证,而boost::lexical_cast就做了这方面的验证。

其实例子中的’\0’在开始的时候,起了不小的误导作用,开始以为是boost::lexical_cast无法处理最后末尾是’\0’的字符串,到现在其实不然,我们把’\0’转换为’a’字符一样会出现这种问题,但是我们使用’\n’,’\r’和空格等字符就不会出现这种问题,现在我们知道其根源就是在字符转换过程中输入流没有输入全部字符,所以流的结束标志EOF,一直为0。

其实在上面的应用中我们不能一直认为boost::lexical_cast的方法一定是好的。在我们编成过程中,常见的转换是把一段字符串中含有数字和字母的字符串中的数字串转换为整形,这样的如果我们使用boost::lexical_cast的话,永远得不到正确结果了,每次都会有异常抛出,这时候我们可以使用stringstream,转换后不判断eof(),这样就可以得到我们想要的整数。
9 7 3 1 2 4 8 :



本类最新行业评测技巧教程学院
本类热点本日本周本月
本类推荐本日本周本月

广告联系 | 版权说明 | 意见建议 | 加入收藏 | 军网站群 [ 军软件园 - 军软件商城 - 军软件园论坛 ]

电信与信息服务业务经营许可证:京ICP证050203