boost.png (6897 bytes)頭文件 boost/lexical_cast.hpp


Motivation 動機

有時一個數值需要被轉換成為文本字符形式, 比如說把一個int值變成一個字符串,反之亦然,有時把一個字符串解釋成一個int值。這樣的例子在程序內的數據類型和程序外部的描述之間的轉換是很常見的,比如說窗口和配置文件。

標準cc++庫提供了一些易用的實現來做這樣的轉換。但是,它們因其易使用程度而變化,另外因其安全性而差異。

舉個例子,對於在標準c家族中的函數,它們有一系列的限制。以atoi為代表:

標準c函數以strtol為代表有同樣的限制,但是在轉換過程上提供更好的支持。儘管如此,大體上來說這樣的控制函數經常要麼不怎麼需要要麼沒有用。Scanf系列函數提供更強的控制,但是同樣缺少安全性和易使用性。

標準C++庫為正要討論的內置格式轉換提供了stringstream。它對從格式化和I/O轉換以及任意類型和字符類型的轉換提供了強有力的控制。但是,使用stringstream進行直接的轉換會是拙劣的(含有對額外的本地變量的說明和缺少中綴表達式便利性)或者晦澀的(stringstream對像在表達式中以臨時對象的形式被創建)。facet為控制文本表達提供了更充分的概念和靈活性,但是它們的高度複雜性和高起點需要(對stringstream)相當高程度的熟悉度---即使是為了簡單的轉換,從而把大多數程序員排除在外了。

Lexical_cast函數模版為以文本表示的任意的類型之間的轉換提供了方便和一致的形式。它提供的簡化形式是在表達式級別上的簡單性易用性。對於更多相關的轉換,比如比lexical_cast提供的默認行為更為精確的或者需要更嚴密的格式轉換控制,這時推薦傳統的stringstream。當在numericnumeric之間轉換的時候,numeric_cast 可能提供了比lexical_cast更好的行為。

關於基於字符串格式化的相關選項和問題的更多討論,包括 stringstream, lexical_cast, 和其它的對照,請見 Herb Sutter 的論文, The String Formatters of Manor Farm.


Examples 例子

下面的例子把一系列數字作為命令行參數:
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(bad_lexical_cast &)
{
args.push_back(0);
}
}
...
}
下面的例子使用字符串表達式來表示數字:
void log_message(const std::string &);

void log_errno(int yoko)
{
log_message("Error " + boost::lexical_cast<std::string>(yoko) + ": " + strerror(yoko));
}

Synopsis 概要

庫的定義在 "boost/lexical_cast.hpp":
namespace boost
{
class bad_lexical_cast;
template<typename Target, typename Source>
Target lexical_cast(const Source& arg);
}
單元測試定義在 "lexical_cast_test.cpp".


lexical_cast

template<typename Target, typename Source>
Target lexical_cast(const Source& arg);
將 arg 輸入到一個標準庫的基於字符串的流,返回其結果並以 Target 對像輸出。其中 Target 要麼是 std::string 要麼是 std::wstring, 對整個字符串的內容進行流提取,包括空格,而非依賴於默認的 operator>> 行為。如果轉換不成功,則拋出一個 bad_lexical_cast 異常。

對於參數和返回類型的要求有:

本文後面的stream中的字符類型默認為char除非Source或者Target需要長字符流,這種情況下本文後面的stream使用wchar_tsource類型中需要的長字符流的是wchar_twchar_t*std::wstring. 需要長字符的Target類型為wchar_tstd::wstring.

當轉換需要更高級的控制時,std::stringstreamstd::wstringstream提供了更合適的途徑。當需要非基於流的轉換時,lexical_cast不是完成這項工作的合適工具,它不用在這類特殊的類型轉換之列。


bad_lexical_cast

class bad_lexical_cast : public std::bad_cast
{
public:
... // std::exception 相同的成員函數接口
};
用來表示 lexical_cast 運行時出錯的異常。

Frequently Asked Questions 常見問題

Q: 為什麼 lexical_cast<int8_t>("127") 會拋出 bad_lexical_cast?
A: 類型 int8_t 是 char 或 signed char 的 typedef. 字面轉換為這些類型,只是從源讀入一個字節,但由於源中有超過一個字節,所以會拋出異常。

請使用其它整數類型,如 intshort int. 如果邊界檢查是重要的,你也可以調用 numeric_cast:

numeric_cast<int8_t>(lexical_cast<int>("127"));

Q: 為什麼一個 int8_tuint8_tlexical_cast<std::string> 不是像我想要那樣?
A: 如上,注意 int8_tuint8_t 其實就是 chars 且被按此格式化。為避免這樣,應首先轉型為一個整數類型:

lexical_cast<std::string>(static_cast<int>(n));

Q: 該實現總是重置底層流對象的 ios_base::skipws 標誌。它破壞了要使用這個標誌的 operator>>. 你是否可以去掉重置該標誌的代碼?
A: 以後的版本有可能這樣做。在 [N1973] 中並不要求重置該標誌,但請記住,[N1973] 尚未被委員會所接受。順便說一句,這是一個讓你的 operator>> 符合標準的好機會。請閱讀一本好的C++書,學習一下 std::sentryios_state_saver.

References 參考

Changes 變更

August, October 2006:

June 2005:

更早期:


c Copyright Kevlin Henney, 2000–2005