第112讲 字符转换

在说boost之前,我们先来看一个小工具,这是一个关于字符转换的工具,他可以取代c库中的atoi这些小函数,但同时他更加安全。

为了实现更加安全和更具有鲁棒性的转换,首先我们写一个自己的异常类,这个异常类主要是针对字符转换,所以我们从std::bad_cast来继承他。


//————————————

// 顾名思义,该类主要是帮助使用者

// 提供一些转换失败的信息

//————————————

class bad_lexical_cast : public std::bad_cast

{

public:

bad_lexical_cast() :

source(&

typeid(void)), target(&

typeid(void))

{

}


bad_lexical_cast(

const std::type_info &

source_type,

const std::type_info &

target_type) :

source(&

source_type), target(&

target_type)

{

}

const std::type_info &

source_type() const

{

return *source;

}


const std::type_info &

target_type() const

{

return *target;

}


virtual const char *what() const throw()

{

return "

bad lexical cast: source type value could not be interpreted as target"

;

}


virtual ~bad_lexical_cast() throw()

{

}

private:

const std::type_info *source;

const std::type_info *target;

};


//—————————————-

// 当我们转换失败的时候就会抛出上面的异常

//—————————————-

接下来我们写一个转换的核心类,在C++里面有个stringstream流,他能够将各种带有operator<<操作符的类型转换为string,同时也能够将string转换带有operator>>操作符的类型,他的用法如下:

//——————————————–

// 这里举两个例子

// 一个是将其他类型转换为string或者是wstring

//——————————————-

template<typename T>

std::wstring ToString(const T&

s)

{

std::ostringstream oss;

oss <<s;


return oss.str();

}


template<typename T>

std::wstring ToWString(const T&

s)

{

std::wostringstream oss;

oss <<s;


return oss.str();

}


template<typename T>

T FromWString(const std::wstring&

s)

{

T x;

std::wistringstream iss(s);

iss >>x;


return x;

}


template<typename T>

T FromString(const std::string&

s)

{

T x;

std::istringstream iss(s);

iss >>x;


return x;

}



其实,到这里我觉得我们已经可以很方便的转换字符问题了,但是,我们还可以做得更好一些,我们来定义一个字符转换的类:


//—————————————-

// class lexical_stream

//—————————————-

template<typename Target, typename Source>

class lexical_stream

{

private:

typedef char char_type;

std::basic_stringstream<char_type>stream;


public:

lexical_stream()

{

stream.unsetf(std::ios::skipws);

if(std::numeric_limits<Target>::is_specialized)

stream.precision(std::numeric_limits<Target>::digits10 + 1);

else if(std::numeric_limits<Source>::is_specialized)

stream.precision(std::numeric_limits<Source>::digits10 + 1);

}


~lexical_stream()

{

}


//把Source类型输入到流中

bool operator<<(const Source &

input)

{

return !(stream <<input).fail();

}


//把流转换为Target类型输出

template<typename InputStreamable>

bool operator>>(InputStreamable &

output)

{

return !std::is_pointer<InputStreamable>::value &

&

stream >>output &

&

stream.get() ==

std::char_traits<char_type>::eof();

}


// string特化

template<>

bool operator>>(std::string &

output)

{

output = stream.str();

return true;

}

};


//—————————————-


我们不难看出,其实这就是将上面我们的小例子更进一步的封装,同时添加了安全安全监测和精度的设置,还特别针对string类型进行了特化,这有一个好处就是当我们string转换到string的时候就变得相当的高效了。


接下来我们要处理数组和指针之间的问题:


//—————————————–

// 将Source重新包装

// 因为在lexical_stream<Target, NewSource>

// 构造函数中会对两个类型进行判断

// 所以有了下面的两个模板

// 当我们这样:

// char ch[] = "

123456"

;

//
int k = lexical_cast<int>(ch);

// 写的时候,他会根据ch数组通过

// template<class T, std::size_t N>

// 等价出ch的类型为char*

// 否则是无法通过编译的

// 这就是将数组退化到指针的方式

//—————————————–

template<class T>

struct array_to_pointer_decay

{

typedef T type;

};


template<class T, std::size_t N>

struct array_to_pointer_decay<T[N]>

{

typedef const T* type;

};



有了这些东西后,我们就可以进一步提取让他变得更加容易使用:


//—————————————-

// lexical_cast

//—————————————-

template<typename Target, typename Source>

Target lexical_cast(const Source &

arg)

{

typedef typename array_to_pointer_decay<Source>::type NewSource;


lexical_stream<Target, NewSource>interpreter;

Target result;


if(!(interpreter <<arg &

&

interpreter >>result))

throw(bad_lexical_cast(typeid(NewSource), typeid(Target)));

return result;

}



//—————————————-

// 下面是演示一下该工具的释放方式

//—————————————-

int main(){

char ch[] = "

123456"

;

int k = lexical_cast<int>(ch);

try{

int i = lexical_cast<int>("

4365"

);

float f = lexical_cast<
float>("

234.546"

);

double d = lexical_cast<double>("

24534.546345"

);

std::string s = lexical_cast<std::string>(24534.546345);

std::cout <<i <<std::endl;

std::cout <<
f <<std::endl;

std::cout <<d <<std::endl;

std::cout <<s <<std::endl;

}

catch (bad_lexical_cast&

e){

std::cout <<e.what() <<std::endl;

}


try{

lexical_cast<int>("

0.335"

);

}

catch (bad_lexical_cast&

e){

std::cout <<"

source type: "

<<e.source_type().name() <<std::endl;

std::cout <<"

target type: "

<<e.target_type().name() <<std::endl;

std::cout <<e.what() <<std::endl;

}


return 0;

}

第112讲  字符转换


那么到这里,我想这一讲要说的内容其实已经完成了,因为我们这一讲本来就是要说的是boost::lexical_cast,而boost::lexical_cast的实现方式就和我们上面所说的是一样的,唯一不同的是如果我们要使用boost的lexical_cast那么我们要包含头文件<boost/lexical_cast.hpp>,我们要捕获的异常为boost::bad_lexical_cast。

在源码都熟悉以后用是不是也就不需要我再说了呢?


ok,这一讲就到这里吧,接下来我们都会说C++对字符串的操作,包括正则表达式。


//======================================

回复D或者d查看目录,回复数字查看相应的章节。


原文始发于微信公众号(

C/C++的编程教室

):第112讲 字符转换

|

发表评论