zoukankan      html  css  js  c++  java
  • 在文字和數字間轉換:boost::lexical_cast « Heresy's Space

    在文字和數字間轉換:boost::lexical_cast « Heresy's Space

    在文字和數字間轉換:boost::lexical_cast

     
    2
     
    0

     

    i
     
    給個評價吧~

     

    Quantcast

    這一篇是來大概介紹一下 Boost C++ Libraries 裡的 lexical_cast 這個函式庫(官網);他的功能相當簡單,主要就是提供了一個 template 函式的介面,來做到任一型別和文字間的轉換。

    很多時候,在寫程式的時候,我們會需要把字串文字(例如 std::string)的資料轉成數值(例如:double),或是反過來,把數值資料轉成文字;尤其是當要從檔案讀取資料,或是要求使用者輸入時,基本上這都是會常常要用到的功能。

    雖然在標準的 C 或 C++ 裡的確有提供部分的函式、例如 atoi()參考),可以快速地做到部分的轉換,但是其實都有相當的限制。例如基本上他們只針對內建的部分型別有提供對應的函式(應該是只有 intlongdouble),而且也沒有反向的轉換(itoa() 並非標準函式,要用標準的寫法要用 sprintf()參考);而另外由於不同的型別就是不同的函示、也很難寫出統一個方法,來針對轉換的型別做擴充。

     

    使用 C++ IO Stream

    而在標準 C++ 的函式庫裡,實際上他有透過 IO Stream 的概念(參考),提供了 stringstream 這個類別(參考),來進行可自訂的、和文字間的轉換。像下面就是一個簡單的例子:

    #include <stdlib.h>
    #include <string>
    #include <sstream>
    #include <iostream>
     
    using namespace std;
     
    int main( int  argc, char** argv )
    {
      // string to float
      string sTmp = "123.456";
      stringstream mSS( sTmp );
      float fTmp;
      mSS >> fTmp;
      cout << "Convert from string to float: " << fTmp << endl;
    
      // float to string
      fTmp = 1234.567f;
      stringstream mSS2;
      mSS2 << fTmp;
      sTmp = mSS2.str();
      cout << "Convert from float to string: " << sTmp << endl;
    }

    第一段的程式碼,基本上就是將 sTmp 這個字串,透過 stringstreamoperator>> 來轉換為浮點數 fTmp;而第二段程式碼,則是反過來,把 fTmp 這個浮點數,轉換成字串、儲存到 sTmp 這個字串裡。基本上,這邊主要就是透過 stringstream 這個「字串流」來做;而他基本的用法,其實和標準輸入、輸出的 cout / cin 是一樣的~差別只在於,他的資料來源和輸出結果,都是字串而已。

    而這邊用的範例是比較簡單,直接用內建的 floatstring 這兩種型別來作範例;而如果是要擴充到其他的自訂型別的話,只要去定義各種型別的 operator<<operator>> 就可以了~下面是一個簡單的範例:

    class CVector2
    {
    public:
      float x;
      float y;
    };
    
    istream& operator>>( istream& iS, CVector2& v )
    {
      iS >> v.x >> v.y;
      return iS;
    }
    
    ostream& operator<<( ostream& oS, const CVector2& v )
    {
      oS << v.x << "/" << v.y;
      return oS;
    }

    這個範例裡定義了一個 2D 的向量 CVector2、裡面有 xy 兩個 float 的成員變數;另外,也定義了 operator<<operator>> 這兩個全域函式,可以用來處理 CVector2 這個自訂型別與 STL stream 間的運作。例如下面就是簡單的範例:

    string sTmp = "12 34";
    stringstream mSS( sTmp );
    CVector2 v;
    mSS >> v;
    cout << v << endl;

    也就是在針對 CVector2 寫好 operator<<operator>> 後,他就擁有標準的 C++ IO stream 的處理能力了!這點算是相當方便的~

    boost::lexical_cast

    不過,如果要直接使用 stringstream 來做的話,其實在操作上會比較繁瑣,所以 Boost C++ Libraries 就提供了所謂的「lexical_cast」,專門來處理這些各種型別和字串間的轉換。

    在使用 lexical_cast 時,必須要先加入「#include <boost/lexical_cast.hpp>」;而它的使用基本上非常簡單,就是一個 template 函式,他的形式是:

    template<typename Target, typename Source>
    Target lexical_cast(const Source &arg);

    而使用上,如果直接修改這篇文章第一個使用 stringstream 的程式的話,就會變成下面的形式。

    // string to float
    string sTmp = "123.456";
    float fTmp = boost::lexical_cast<float>( sTmp );
    cout << "Convert from string to float: " << fTmp << endl;
    
    // float to string
    fTmp = 1234.567f;
    sTmp = boost::lexical_cast<string>( fTmp );
    cout << "Convert from float to string: " << sTmp << endl;

    基本上,只要把要轉換的資料當參數傳進去,在指定要轉換的目標型別,就可以了!他省去了所有對於 stringstream 的操作,而直接用一個統一的函式來做,在許多時候會是相當方便的~

    而如果要讓自訂的型別也可以適用於 lexical_cast 的話,來源的型別(Source)和目標的型別(Target)分別需要符合一些條件:

    • 來源型別必須要是「OutputStreamable」,也就是有定義 std::ostreamstd::wostreamoperator<<
    • 目標型別必須要是「InputStreamable」,也就是要有針對 std::istreamstd::wistream 定義 operator>>
    • 目標型別必須要有 default constructor 和 copy constructor。

    所以基本上,只要有針對自己定義的型別,也定義出 IO Stream 的 operator<<operator>>,應該就可以用了!

    一些小細節

    不過實際上 Heresy 在用的時候,還是有遇到一些問題。像以前面 CVector2 的例子來說,雖然已經有定義了 operator<<operator>>,但是還是會有問題的!像下面的例子,想直接使用 lexical_cast 把字串 sTmp 轉換成為 CVector2 的話,語法雖然沒問題,但是在執行時是會出錯的!

    string sTmp = "12 34";
    CVector2 v = boost::lexical_cast< CVector2 >( sTmp );

    主要的原因呢,應該是由於 Boost 的 lexical_cast 會去設定使用的 istreamnoskipws、不忽略空白(參考);而也因此,才會造成 istream& operator>>( istream& iS, CVector2& v ) 這個函式在直接使用 stringstream 的時候因為預設會略調空白的資料、而可以正常運作,但是使用 lexical_cast 時卻因為特別把空白也抓出來做處理了,反而造成轉換的錯誤。

    而解決方法呢?其實也滿簡單的,只要在進行處理前,先強制設定回 skipws 應該就可以了~修正後的函式,也就變成了:

    istream& operator>>( istream& iS, CVector2& v )
    {
      iS >> std::skipws >> v.x >> v.y;
      return iS;
    }

    理論上,這樣應該就不會有問題、可以直接用 lexical_cast 來處理自訂的 CVector2 了~

    不過另外要注意的是,實際上 Boost 的 lexical_cast 在做處理的時候,做了很多例外狀況的判斷、處理,算是相當嚴謹的;而也因此,他的效能並不會特別好!有興趣的話,可以參考看看《Very poor boost::lexical_cast performance》這串討論。不過基本上,由於它的效率真的比較差一些,所以如果是轉換的效能很重要的話,建議還是換比較快的方法來寫會比較合適。

  • 相关阅读:
    我的知识库(4) java获取页面编码(Z)
    知识库(3)JAVA 正则表达式 (超详细)
    The Struts dispatcher cannot be found. This is usually caused by using Struts tags without the associated filter. Struts
    某人总结的《英语听力的技巧 》,挺搞的
    我的知识库(5)java单例模式详解
    构建可扩展程序
    SerialPort (RS232 Serial COM Port) in C# .NET
    Python学习笔记——String、Sequences
    UI题目我的答案
    jQuery学习系列学会操纵Form表单元素(1)
  • 原文地址:https://www.cnblogs.com/lexus/p/2593673.html
Copyright © 2011-2022 走看看