在说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;

}

字符转换_字符转换


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

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


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