lexical_cast

优质
小牛编辑
141浏览
2023-12-01

头文件: "boost/lexical_cast.hpp"

所有应用都会使用字面转换。我们把字符串转为数值,反之亦然。许多用户定义的类型可以转换为字符串或者由字符串转换而来。你常常是在需要这些转换时才编写代码,而更好的方法是提供一个可重用的实现。这就是 lexical_cast的用途所在。你可以把lexical_cast 想象为使用一个 std::stringstream 作为字符串与数值的表示之间的翻译器。这意味着它可以与任何用operator<<进行输出的源以及任何用operator<<进行输入的目标一起工作。这个要求对于所有内建类型与多数用户自定义类型(UDTs)都可以做到。

用法

lexical_cast 在类型之间进行转换,就象其它的类型转换操作一样。当然,使它得以工作的必须是一个转换函数,但从概念上说,你可以把它视为转型操作符。比起调用一堆的转换子程序,或者是编写自己的转换代码,lexical_cast 可以更好地为任何满足它的要求的类型服务。它的要求就是,源类型必须是可流输出的(OutputStreamable),而目标类型必须是可流输入的 (InputStreamable)。另外,两种类型都必须是可复制构造的(CopyConstructible),并且目标类型还要是可缺省构造的 (DefaultConstructible)和可赋值的(Assignable)。可流输出(OutputStreamable)意味着存在一个为该类 型定义的operator<<,可流输入(InputStreamable)则要求有一个operator>>. 对于许多类型,包括所有内建类型和标准库中的字符串类型,这个条件都满足。要使用 lexical_cast, 就要包含头文件 "boost/lexical_cast.hpp".

让 lexical_cast 工作

我不想通过跟你示范手工编写转换用的代码来说明 lexical_cast 如何节省了你的时间,因为我可以很肯定你一定写过这样的转换代码,并且很可能不只一次。相反,只用一个例子来示范如何使用 lexical_cast 来进行通用的(字面上的)类型转换。

#include <iostream>
#include <string>
#include "boost/lexical_cast.hpp"

int main() {
  // string to int
  std::string s="42";
  int i=boost::lexical_cast<int>(s);

  // float to string
  float f=3.14151;
  s=boost::lexical_cast<std::string>(f);

  // literal to double
  double d=boost::lexical_cast<double>("2.52");

  // 失败的转换
  s="Not an int";
  try {
    i=boost::lexical_cast<int>(s);
  }
  catch(boost::bad_lexical_cast& e) {
    // 以上lexical_cast将会失败,我们将进入这里
  }
}

这个例子仅仅示范了多种字面转换情形中的几种,我想你应该同意为了完成这些工作,通常你需要更多的代码。无论何时你不确定转换是否有效,都应该用一个try/catch 块来保护 lexical_cast ,就象你在这个例子看到的那样。你可能注意到了没有办法控制这些转换的格式;如果你需要这种级别的控制,你要用 std::stringstream

如果你曾经手工进行过类型间的转换,你应该知道,对于不同的类型,需要使用不同的办法来处理转换以及可能出现的转换失败。这不仅是有点不便而已,它还妨碍了用泛型代码执行转换的努力。稍后我们将看到 lexical_cast 如何帮助你实现这一点。

这个例子中的转换用手工来实现也非常简单,但可能会失去转型操作的美观和优雅。而 lexical_cast 做起来更简单,并且更美观。再考虑一下lexical_cast对与之一起工作的类型所需的简单要求。考虑到对所有符合该要求的类型的转换可以在一行代码内完成的事实。再结合该实现依赖于标准库的stringstream这一事实[15],你可以看到 lexical_cast 不仅是执行字面转换的便利方法,它更是C++编译艺术的一个示范。

[15] 事实上,对于某些转换,有一些优化的方法可以避免使用 std::stringstream 带来的额外开销。当然,你可以在需要的时候对你自己的类型定制它的行为。

用 lexical_cast 进行泛型编程

作为使用lexical_cast进行泛型编程的简单例子,来看一下如何用它创建一个 to_string 函数。这个函数接受任何类型的参数(当然它要符合要求)并返回一个表示该值的 string 。标准库的用法当然也可以在std::stringstream的帮助下用几行代码完成这个任务。在这里,我们使用lexical_cast 来实现,只需要一个前转换函数调用及一些错误处理。

#include <iostream>
#include <string>
#include "boost/lexical_cast.hpp"

template <typename T> std::string to_string(const T& arg) {
  try {
    return boost::lexical_cast<std::string>(arg);
  }
  catch(boost::bad_lexical_cast& e) {
    return "";
  }
}

int main() {
  std::string s=to_string(412);
  s=to_string(2.357);
}

这个小程序不仅易于实现,它还因为lexical_cast而增加了价值。

使类可以用于 lexical_cast

因为 lexical_cast 仅要求它所操作的类型提供适当的 operator&lt;&lt;operator&gt;&gt; ,所以很容易为用户自定义类型增加字面转换的支持。一个可以同时作为lexical_cast的目标和源的简单UDT看起来就象这样:

class lexical_castable {
public:
  lexical_castable() {};
  lexical_castable(const std::string s) : s_(s) {};

  friend std::ostream operator<<
    (std::ostream& o, const lexical_castable& le);
  friend std::istream operator>>
    (std::istream& i, lexical_castable& le);

private:
  virtual void print_(std::ostream& o) const {
    o << s_ <<"\n";
  }

  virtual void read_(std::istream& i) const {
    i >> s_;
  }

  std::string s_;
};

std::ostream operator<<(std::ostream& o,
  const lexical_castable& le) {
  le.print_(o);
  return o;
}

std::istream operator>>(std::istream& i, lexical_castable& le) {
  le.read_(i);
  return i;
}

lexical_castable 类现在可以这样用了:

int main(int argc, char* argv[]) {
  lexical_castable le;
  std::cin >> le;

  try {
    int i = boost::lexical_cast<int>(le);
  }
  catch(boost::bad_lexical_cast&) {
     std::cout << "You were supposed to enter a number!\n";
  }
}

当然,输入和输出操作符最好可以允许这个类于于其它流。如果你使用标准库的IOStreams,或者其它使用 operator&lt;&lt;operator&gt;&gt;的库,你可能已经有很多可以用于 lexical_cast 的类。它们不需要进行修改。直接对它们进行字面转换就行了!

总结

lexical_cast 是用于字符串与其它类型之间的字面转换的一个可重用及高效的工具。它是功能性和优雅性的结合,是杰出程序员的伟大杰作[16]。 不要在需要时实现小的转换函数,更不要在其它函数中直接插入相关逻辑,应该使用象 lexical_cast 这样的泛型工具。它有助于使代码更清晰,并让程序员专注于解决手上的问题。

[16] 我知道,我总是很傲慢的,我们这些程序员,工作中常常需要数学、物理学、工程学、建筑学,和其它一些艺术和学科。这会使人畏缩,但也有无穷的回报。

以下情况时使用lexical_cast:

  • 从字符串类型到数值类型的转换

  • 从数值类型到字符串类型的转换

  • 你的自定义类型所支持的所有字面转换