2012-02-20 2 views
2

std::stringをC++のいくつかの数値型に構文解析するのに最適な方法は、対象の型が事前にわかっていない場合です。不明な型の数値文字列を解析しますか?

私はlexical_castを見ましたが、それはテンプレートタイプとしてターゲットタイプを取ります。私はbad_lexical_castをキャッチしてfalseを返すことでこれを乱用するラッパー関数を書くことができましたが、それは醜いようです。

私の入力値は通常、intまたはfloatであり、非常にシンプルな書式設定をしていますが、柔軟なものは素晴らしいでしょう。

+1

例外をキャッチすることは悪用だと思いますが、btwですが、タイプごとに*を書くことはしません。むしろ、すべての許可されたフォーマットを試みる単一の機能を持っています。例外は、文字列が予想される形式と一致しないことを示す完全に合理的な方法です。適切なフォーマットを知らないことを知っている関数の外に決して伝播する必要はなく、いくつかの異なるフォーマットを順番に試して「リスクを冒す」必要があります。 –

答えて

3

ブーストスピリットNumerical Parsersまたは(ab)ブーストレキシカルキャストを使用できます。

ブーストスピリットは、許容されるフォーマットをきめ細かく制御できるようにします。ここで

また、あなたがいくつかの可能な数値の入力フォーマット(徐々に)を検出し、マッチしたタイプを返すことができる方法を示して、迅速なデモです。もちろんそれは過度のものかもしれませんが、スピリットをさらにどのように使うべきかを示すべきです。

デモでは、入力イテレータを進めて、数値入力が終了した場所での解析を簡単に続ける方法も示しています。

#include <boost/spirit/include/qi.hpp> 
#include <boost/spirit/include/phoenix.hpp> 
namespace qi = boost::spirit::qi; 

enum numeric_types 
{ 
    fmt_none, 
    fmt_float, 
    fmt_double, 
    fmt_uint, 
    fmt_int, 
    // fmt_hex, etc. 
}; 

template <typename It> 
    bool is_numeric(It& f, It l, numeric_types& detected) 
{ 
    return qi::phrase_parse(f,l, 
      qi::uint_ [ qi::_val = fmt_uint ] 
      | qi::int_ [ qi::_val = fmt_int ] 
      | qi::float_ [ qi::_val = fmt_float ] 
      | qi::double_ [ qi::_val = fmt_double ] 
      ,qi::space, detected); 
} 

template <typename It> 
    bool is_numeric(It& f, It l) 
{ 
    numeric_types detected = fmt_none; 
    return is_numeric(f, l, detected); 
} 

int main() 
{ 
    const std::string input = "124, -25, 582"; 
    std::string::const_iterator it = input.begin(); 

    bool ok = is_numeric(it, input.end()); 

    if (ok) 
    { 
     std::cout << "parse success\n"; 
     if (it!=input.end()) 
      std::cerr << "trailing unparsed: '" << std::string(it,input.end()) << "'\n"; 
    } 
    else 
     std::cerr << "parse failed: '" << std::string(it,input.end()) << "'\n"; 

    return ok? 0 : 255; 
} 
+0

ワンノート:Qiを使用すると、さらに一歩進むことができます。 'boost :: variant 'を使うと、型を検出するだけでなく、解析された結果を一度に保存することができます。私はそれをフックする方法をよく覚えていませんが、代わりと変種を確実に組み合わせることができます:) –

+0

@MatthieuM。私はそうしますが、それは私が読んでいるように問題ではありませんでした。実際には 'qi :: double_'を他のオプションとマッチさせるのが普通です。また、パーザを静的に格納します。そして 'qi :: wrap'でラップしてください...しかし、私は先に進んでいます – sehe

1

あなたが実際にそれを変換するためのデータを解析すると、あなたが結果を置くに タイプを知っておく必要があります。 C++は静的に型付けされた言語、 であり、その回避策はありません。あなたは、文字列を持っている、そしてそれは、正規表現を使用して、あるどのようなタイプ をお知りになりたい場合は、単純なソリューションです:

"\\s*[+-]?(?:" 
    "\\d+\\.\\d*(?:[Ee][+-]?\\d+)?" 
    "|\\.\\d+(?:[Ee][+-]?\\d+)?" 
    "|\\d+[Ee][+-]?\\d+" 
")" 

は、すべての可能な浮動小数点値と一致する必要があり、かつ:

"\\s*[+-]?(?:" 
    "[1-9][0-9]*" 
    "|0[0-7]*" 
    "|0x[0-9a-fA-F]+" 
)" 

は一致して任意の基数の整数。 (BoostまたはC++ 11の正規表現のデフォルト設定を仮定しています)

+1

そして、これらの正規表現が(オーバーフローなどにより)無効な文字列にマッチするように注意してください。とにかく検証する。 –

+1

@SteveJessopはい。変換したいものを設定したら、変換が正しく機能していることを確認する必要があります。正規表現では、ターゲットの種類と構文チェックのみを選択できます。意味論的検証を手助けすることはできません。 –

関連する問題