まったく異なるアプローチを提供したいと思います。入力文字列を取得し、それを自分でトークン化し、 を使用して個々のフィールドを変換しますboost::lexical_cast<T>
。
理由: スペースで区切られた 2 つの int フィールドと 2 つの double フィールドを含む文字列の解析に午後を無駄にしました。次のことを行います。
int i, j;
double x, y;
std::istringstream ins{str};
ins >> i >> j >> x >> y;
// how to check errors???...
次のような正しい入力を解析します
`"5 3 9.9e+01 5.5e+02"`
正しく、しかしこれで問題を検出しません:
`"5 9.6e+01 5.5e+02"`
i
5 (OK) にj
設定され、9 (??) に設定され、 6.0 (= 0.6e x
+01) に設定され、 y
550 (OK) に設定されます。設定されていないことに非常に驚きましたfailbit
... (プラットフォーム情報: OS X 10.9、Apple Clang++ 6.0、C++11 モード)。
もちろん、「でも待ってください。標準はそうあるべきだと述べています」と言うことができます。あなたは正しいかもしれませんが、それがバグではなく機能であることを知っていても、適切なエラーを実行したい場合の痛みは軽減されません。何マイルものコードを書かずにチェックします。
OTOHさん、「マリウス」の優れたトークナイザー機能を使って、最初に空白で分割するとstr
、突然すべてが非常に簡単になります。これは、トークナイザーのわずかに変更されたバージョンです。文字列のベクトルを返すように書き直しました。オリジナルは、文字列に変換可能な要素を持つコンテナーにトークンを配置するテンプレートです。(このような一般的なアプローチが必要な場合は、上記の元のリンクを参照してください。)
// \param str: the input string to be tokenized
// \param delimiters: string of delimiter characters
// \param trimEmpty: if true then empty tokens will be trimmed
// \return a vector of strings containing the tokens
std::vector<std::string> tokenizer(
const std::string& str,
const std::string& delimiters = " ",
const bool trimEmpty = false
) {
std::vector<std::string> tokens;
std::string::size_type pos, lastPos = 0;
const char* strdata = str.data();
while(true) {
pos = str.find_first_of(delimiters, lastPos);
if(pos == std::string::npos) {
// no more delimiters
pos = str.length();
if(pos != lastPos || !trimEmpty) {
tokens.emplace_back(strdata + lastPos, pos - lastPos);
}
break;
} else {
if(pos != lastPos || !trimEmpty) {
tokens.emplace_back(strdata + lastPos, pos - lastPos);
}
}
lastPos = pos + 1;
}
return tokens;
}
そして、次のように使用します(ParseError
いくつかの例外オブジェクトです):
std::vector<std::string> tokens = tokenizer(str, " \t", true);
if (tokens.size() < 4)
throw ParseError{"Too few fields in " + str};
try {
unsigned int i{ boost::lexical_cast<unsigned int>(tokens[0]) },
j{ boost::lexical_cast<unsigned int>(tokens[1]) };
double x{ boost::lexical_cast<double>(tokens[2]) },
y{ boost::lexical_cast<double>(tokens[3]) };
// print or process i, j, x, y ...
} catch(const boost::bad_lexical_cast& error) {
throw ParseError{"Could not parse " + str};
}
注: 必要に応じてBoost スプリットまたはトークナイザーを使用できますが、マリウスのトークナイザーよりも低速でした (少なくとも私の環境では)。
更新: 代わりにboost::lexical_cast<T>
C++11 の " " 関数を使用できますstd::sto*
(たとえばstoi
、文字列トークンを int に変換する場合)。std::invalid_argument
これらは、変換を実行できなかったstd::out_of_range
場合と、変換された値を表現できない場合の2 種類の例外をスローします。これらを個別にキャッチすることも、それらの親をキャッチすることもできますstd::runtime_error
。上記のコード例の変更は、読者への演習として残されています :-)