2012-03-22 12 views
20

多くの文字列から値をスキャンする必要があるとき、シンプルさと使いやすさのためにCのsscanf()に厳密に戻っていることがよくあります。たとえば、私は非常に簡潔に2つの値を文字列から2つ引き出すことができます:より安全で使いやすく柔軟なC++の代わりにsscanf()

string str; 
double val1, val2; 
if (sscanf(str.c_str(), "(%lf,%lf)", &val1, &val2) == 2) 
{ 
    // got them! 
} 

これは明らかにC++ではありません。必ずしも憎悪を考慮するとは限りませんが、私はいつも共通の仕事をするより良い方法を探しています。私は文字列を読む "C++の方法"がistringstreamであることを理解していますが、上の書式文字列で括弧とカンマを処理するために必要な余分な型付けは、あまりにも煩わしいので使いたくないのです。

上記のような方法で組み込み施設を私の意志に曲げる良い方法はありますか、上記のことをより安全な方法で行う良いC++ライブラリがありますか? Boost.Formatは実際に出力の問題をうまく解決したようですが、入力には同様に簡潔なものは見つかりませんでした。

+0

こんにちは、私は本当にブーストが何かを持っていると思います。今では私の指はそれのために私自身のライブラリを作るのがかゆいです... –

+1

FWIW、私は何か他のものと同じように "C++"と思っています - それは能力に限界があります(iostreamsほど構文的にはひどいものではありません)。私はvariadicテンプレートの観点からCのフォーマット関数を実装する提案を見てきました(したがって、C++ 11のみ)。それは演奏することができれば、これは大きな改善となるでしょう。良い小さなプロジェクト - 完了したら教えてください。 ; ^) – mcmcc

+0

@mcmcc:実際には、バリデーショナルテンプレートを使って 'printf'を実装するのは、位置引数を除けばかなり簡単です。私は 'sscanf'と同じ問題を期待します。それとは別に、パフォーマンスに問題は見られません。何かがあれば、部分的なインライン展開が本当に有益なことがあります。 –

答えて

13

にフォームの近くでの解析ルールを記述することができ、私は、文字列と文字リテラルで読むことができるコードのビットを書いたということです。通常のストリーム読み込みと同様に、無効なデータを取得した場合、ストリームのバッドビットを設定します。これは、ワイドストリームを含むすべてのタイプのストリームに有効です。新しいヘッダーで、このビットをスティック:

#include <iostream> 
#include <string> 
#include <array> 
#include <cstring> 

template<class e, class t, int N> 
std::basic_istream<e,t>& operator>>(std::basic_istream<e,t>& in, const e(&sliteral)[N]) { 
     std::array<e, N-1> buffer; //get buffer 
     in >> buffer[0]; //skips whitespace 
     if (N>2) 
       in.read(&buffer[1], N-2); //read the rest 
     if (strncmp(&buffer[0], sliteral, N-1)) //if it failed 
       in.setstate(in.rdstate() | std::ios::failbit); //set the state 
     return in; 
} 
template<class e, class t> 
std::basic_istream<e,t>& operator>>(std::basic_istream<e,t>& in, const e& cliteral) { 
     e buffer; //get buffer 
     in >> buffer; //read data 
     if (buffer != cliteral) //if it failed 
       in.setstate(in.rdstate() | std::ios::failbit); //set the state 
     return in; 
} 
//redirect mutable char arrays to their normal function 
template<class e, class t, int N> 
std::basic_istream<e,t>& operator>>(std::basic_istream<e,t>& in, e(&carray)[N]) { 
     return std::operator>>(in, carray); 
} 

そしてそれは、入力された文字が非常に簡単になります。

std::istringstream input; 
double val1, val2; 
if (input >>'('>>val1>>','>>val2>>')') //less chars than scanf I think 
{ 
    // got them! 
} 

PROOF OF CONCEPT。今すぐcinの文字列と文字リテラルを入力できます。入力が完全一致でない場合は、正しく入力できなかった他のタイプと同じように動作します。これは、最初の文字ではない文字列リテラルの空白にのみ一致することに注意してください。唯一の4つの機能は、すべて脳死の単純な機能です。

EDIT

ストリームを解析することは悪い考えです。正規表現を使用してください。

+0

@JasonRそこでは、動作するオーバーロードの解像度が得られたので、他のすべての入力と同様に '>>'を使用します。 –

+0

私はそれが好きです。 sscanfやBoost.Formatのように書式文字列と引数を別々に指定できるようにするのが好きですが、これは私が見た中で最も良い解決策です。良くやった。 –

+0

@JasonR:実際これが私のやるべきことですが、文字列解析をバイパスする方法が不思議だったので、文字列を分割することができました。あなたが言うように、物事を特定の方法で書式設定することはまだ難しいです。私はそれに対処できるのだろうかと思う。私が何かを考えたら、私は再びコメントします。 –

3

正規表現を使うと簡単にできると思います。だからboost :: regexやstd :: regexを新しい標準にしてください。その後、lexical_castまたはストリームを直接使用して、トークンを浮動小数点に変換します。

+0

ああ...それは動作しますが、確かにエレガントではありません。 –

6

私はこれまで文字列解析に使用したことの中で最も良いことはboost.spiritです。それは速く、安全で、非常に柔軟です。大きな利点は、あなたがEBNF文法

using namespace boost::spirit; 

boost::fusion::vector < double, double > value_; 

std::string string_ = "10.5,10.6 "; 

bool result_ = qi::parse(
    string_.begin(), 
    string_.end(), 
    qi::double_ >> ',' >> qi::double_, // Parsing rule 
    value_); // value 
+1

とかっこ –

+1

ありがとう。私はそれが私の必要性のために 'sscanf()と同等であるとは言いません。私はそれが非常に強力であると確信しています(私は精神や "EVNF文法"に精通していません)が、私の目的のために私は変更したいと思うほど簡単ではありません。 –

+0

@JasonR:Extended Backus-Naur Form(ISO/IEC 14977)。 – MSalters

関連する問題