• 在文字和數字間轉換: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》這串討論。不過基本上,由於它的效率真的比較差一些,所以如果是轉換的效能很重要的話,建議還是換比較快的方法來寫會比較合適。

  • 相关阅读:
    魔术方法详解、魔术变量、预定义常量、预定义变量
    RabbitMQ:生产者消费者模型构建/为消息自定义属性/自定义消费者
    RabbitMQ:相关概念
    RabbitMQ:基于centos7安装
    Spring Boot:整合Dubbo
    Dubbo:服务提供者、消费者相关配置
    Dubbo:Dubbo Admin环境搭建
    Zookeeper:Curator Watcher机制和事务控制
    Zookeeper:Curator增删改查API
    Zookeeper:集群和zab协议
  • 原文地址:https://www.cnblogs.com/lexus/p/2593673.html
Copyright © 2020-2023  润新知