顾名思义,lexical_cast库进行”字面值“的转换,类似C中的atoi()函数,可以进行字符串与整数/浮点数之间的字面转换
有关基于字符串的格式化所涉及的选项和问题的详细讨论,包括stringstream、lexical_cast和其他格式的比较,请参阅Herb Sutter的文章the String Formatters of Manor Farm。另外,请查看Performance部分。
boost/lexical_cast.hpp中定义的库特性:
namespace boost
{
class bad_lexical_cast;
template<typename Target, typename Source>
Target lexical_cast(const Source& arg);
template <typename Target>
Target lexical_cast(const AnyCharacterType* chars, std::size_t count);
namespace conversion
{
template<typename Target, typename Source>
bool try_lexical_convert(const Source& arg, Target& result);
template <typename AnyCharacterType, typename Target>
bool try_lexical_convert(const AnyCharacterType* chars, std::size_t count, Target& result);
} // namespace conversion
} // namespace boost
lexical_cast使用类似C++标准转型操作符的形式给出了通用、一致、可理解的语法,它常用的两种形式是
// 标准形式,转换数字和字符
template<typename Target, typename Source>
Target lexical_cast(const Source& arg);
lexical_cast(...)
// 转换C字符串
template <typename Target>
Target lexical_cast(const AnyCharacterType* chars, std::size_t count);
*
等其他字符类型,它只接受一个模板参数Target,指明转换后的目标类型。参数chars和counts则标记了要转换的字符串的范围虽然lexical_cast的用法看起来像转型操作符,但它仅仅是在用法上模仿了转型操作而已,实际上是一个模板函数
lexcial_cast的内部使用了标准的流操作,因此它对转换对象有如下要求:
C++的内建类型和std::string都满足上面的条件,它们也是lexcial_cast最常用的工作搭档
对于标准容器和其他自定义类型,这些函数一般都不满足,所以不能使用lexcial_cast(除非做特殊处理)
基础流的字符类型被假定为char,除非Source或Target需要宽字符流,在这种情况下,基础流使用wchar_t。以下类型也可以使用char16_t或char32_t进行宽字符流:
char16_t *, char32_t *, const char16_t *, const char32_t *
boost::iterator_range
:其中WideCharPtr是一个指向宽字符的指针或指向const宽字符的指针boost::array 、std::array, boost::array 、std::array
如果需要对转换进行更高程度的控制,则std::stringstream和std::wstringstream提供了更合适的路径。当需要非基于流的转换时,不要lexical_cast,它不是这种场景的专用工具。
注意:许多编译器和运行时库无法使用新的Unicode字符进行转换。在使用新类型之前,请确保以下代码编译并输出非零值:
std::cout
<< boost::lexical_cast<std::u32string>(1.0).size()
<< " "
<< boost::lexical_cast<std::u16string>(1.0).size();
使用lexical_cast可以很容易的在数值和字符串之间转换,只需要在目标参数里面指出要转换的目标类型即可:
#include
#include
using namespace boost;
int main(int /*argc*/, char * argv[]) {
int x = lexical_cast<int>("100");
long y = lexical_cast<long>("2000");
float pai = lexical_cast<float>("3.14159e5");
double e = lexical_cast<double>("2.71828");
double f = lexical_cast<double>("1.414.x", 5);
std::cout << x << "\t" << y << "\t" << pai << "\t" << e << "\t" << f << "\n";
std::string str = lexical_cast<std::string>(345);
std::cout << str << "\n";
std::cout << lexical_cast<std::string>(0.618) << "\t" << lexical_cast<std::string>(0x10);
return 0;
}
lexical_cast<bool>(1);
class bad_lexical_cast : public std::bad_cast
{
public:
... // same member function interface as std::exception
};
try
{
cout << lexical_cast<int>("0x100");
cout << lexical_cast<double>("HelloWorld");
cout << lexical_cast<long>("1000L");
cout << lexical_cast<bool>("false") << endl;
}
catch (bad_lexical_cast& e)
{
cout << "error:" << e.what() << endl;
}
template <typename Target, typename Source>
inline bool try_lexical_convert(const Source& arg, Target& result)
template <typename Target, typename CharacterT>
inline bool try_lexical_convert(const CharacterT* chars, std::size_t count, Target& result)
int x;
assert(!conversion::try_lexical_convert("0x100", x));
template<typename T>
bool num_valid(const char *str)
{
T tmp;
return conversion::try_lexical_convert(str, tmp);
}
assert( num_valid<double>("3.14"));
assert(!num_valid<int>("3.14"));
assert( num_valid<int>("65535"));
#include
#include
using namespace boost;
using namespace std;
class demo_class
{
friend std::ostream& operator<<(std::ostream& os, const demo_class& x)
{
os << "demo_class's Name";
return os;
}
};
int main(int /*argc*/, char * argv[]) {
cout << lexical_cast<string>(demo_class()) << endl;
return 0;
}
这段代码具有通用性,值得把它提取为一个模板类。我们可以仿照boost.operator库,定义一个模板类outable,以简化<<的重置。注意,这里没有使用基类链技术,不能用于operator的基类串联,但可以很容易的添加这个功能:
template<typename T>
struct outable
{
friend std::ostream& operator<<(std::ostream& os, const T& x)
{
os << typeid(T).name();
return os;
}
};
这样,任何继承outable的类,就会自动获得流输出操作符或者lexcial_cast的支持:
class demo_class : public outable<demo_class>{};
int main(int /*argc*/, char * argv[]) {
cout << lexical_cast<string>(demo_class()) << endl;
return 0;
}
这里只是用来typeid(T)来输出类的名字,如果想要扩展outable< T>的功能,可以考虑模板类型T提供to_string()或者print()之类的函数来输出特定信息。
在大多数情况lexical_cast就可以满足需求。然而,一些开发人员希望创建自己的转换函数,重用boost::lexical_cast的所有优化。这就是boost::conversion::try_lexical_convert函数的作用所在。
如果转换成功,Try_lexical_convert返回true,否则返回false。如果转换失败且返回false,则结果输出变量的状态为未定义。
实际上,boost::lexical_cast是使用try_lexical_convert实现的:
template <typename Target, typename Source>
inline Target lexical_cast(const Source &arg)
{
Target result;
if (!conversion::try_lexical_convert(arg, result))
throw bad_lexical_cast();
return result;
}
try_lexical_convert放宽了Target类型的CopyConstructible和DefaultConstructible要求。对目标和来源仍有以下要求:
assert(stoi(" 42 ") == 42); //允许有空格
assert(stol("100L") == 100L); //允许L等后缀
assert(stol("1000 9") == 1000L); //后面的被忽略
assert(stod("3.14ispai") == 3.14); //遇到无效字符时停止
assert(to_string(776ul) == "776");
cout << stoul("x100"); // std::invalid_argument
cout << stoi("9999999999"); // std::out_of_range
template<typename T>
T std_lexical_cast(const std::string& s);
template<>
int std_lexical_cast<int>(const std::string& s)
{
return stoi(s);
}
template<>
long std_lexical_cast<long>(const std::string& s)
{
return stol(s);
}
void case5()
{
assert(std_lexical_cast<int>(" 10 ") == 10);
assert(std_lexical_cast<long>("100L") == 100L);
}
下面的示例将命令行参数视为数字数据序列
#include
#include
#include
int main(int /*argc*/, char * argv[])
{
using boost::lexical_cast;
using boost::bad_lexical_cast;
std::vector<short> args;
while (*++argv){
try {
args.push_back(lexical_cast<short>(*argv));
} catch (const bad_lexical_cast&) {
args.push_back(0); //如果转换失败则塞入一个0
}
}
for(auto i : args){
std::cout << i << "\t";
}
}
下面的例子在字符串表达式中使用数字数据:
#include
#include
void log_message(const std::string &i){
std::cerr << i;
};
void log_errno(int yoko)
{
log_message("Error " + boost::lexical_cast<std::string>(yoko) + ": " + strerror(yoko));
}
int main(int /*argc*/, char * argv[])
{
log_errno(1);
}
以下示例转换一些数字并将其放入文件:
void number_to_file(int number, std::FILE* file)
{
typedef boost::array<char, 50> buf_t; // You can use std::array if your compiler supports it
buf_t buffer = boost::lexical_cast<buf_t>(number); // No dynamic memory allocation
std::fputs(buffer.begin(), file);
}
下面的例子接受字符串的一部分并将其转换为int:
int convert_strings_part(const std::string& s, std::size_t pos, std::size_t n)
{
return boost::lexical_cast<int>(s.data() + pos, n);
}
在本例中,我们将创建一个stringize方法,该方法接受序列,将序列的每个元素转换为字符串,并将该字符串附加到结果中。
示例是基于Antony Polukhin的Boost c++ Application Development Cookbook中的示例
#include
struct stringize_functor{
private:
std::string& result;
public:
explicit stringize_functor(std::string& res) : result(res){}
template<typename T>
void operator() (const T& v) const{
result += boost::lexical_cast<std::string>(v);
}
};
#include
template <class Sequence>
std::string stringize(const Sequence& seq) {
std::string result;
boost::fusion::for_each(seq, stringize_functor(result));
return result;
}
#include
#include
int main(int /*argc*/, char * argv[])
{
boost::tuple<char, int, char, int> decim('-', 10, 'e', 5);
if (stringize(decim) != "-10e5") {
return 1;
}
std::pair<int, std::string> value_and_type(270, "Kelvin");
if (stringize(value_and_type) != "270Kelvin") {
return 2;
}
}
在这个例子中,我们将创建一个to_long_double方法来转换 Boost.Variant到long double
#include
#include
struct to_long_double_functor: boost::static_visitor<long double> {
template<typename T>
long double operator() (const T& v) const{
// lexical_cast转换有许多优化,包括对通常发生在泛型编程中的情况的优化,如std::string到std::string或算术类型到算术类型的转换。
return boost::lexical_cast<long double>(v);
}
};
// 如果变量的值不能转换为' long double ',抛出' boost::bad_lexical_cast '
template <class Variant>
long double to_long_double(const Variant& v) {
return boost::apply_visitor(to_long_double_functor(), v);
}
int main(int /*argc*/, char * argv[]) {
boost::variant<char, int, std::string> v1('0'), v2("10.0001"), v3(1);
const long double sum = to_long_double(v1) + to_long_double(v2) + to_long_double(v3);
if (11 < sum && sum < 11.1) {
return 0; // OK, as expected
};
return 1; // FAIL
}