5

次の関数では、 type を読み取れるかどうか、および入力が後で完全に消費されるかどうかを確認することで、文字列sが型に変換可能かどうかを確認しようとしています。私が欲しいTT

template <class T>
bool can_be_converted_to(const std::string& s, T& t) 
{ 
  std::istringstream i(s);
  i>>std::boolalpha;
  i>>t;
  if (i and i.eof())
    return true;
  else
    return false;
}

ただし、関数の最後で is が false であるcan_be_converted_to<bool>("true")ため、false と評価されます。i.eof()

関数が文字列全体を読み取ったとしても、文字列の末尾を超えて読み取りを試みていないため、これは正しいことです。(したがって、istringstreamこれらを読み取るときに最後を過ぎて読み取るため、明らかにこの関数は int および double に対して機能します。)

したがって、実際にチェックする必要があると仮定すると(i and <input completely consumed>):

Q: eof() を使用せずに入力が完全に消費されたことを確認するにはどうすればよいですか?

4

4 に答える 4

8

peek()またはを使用get()して、ストリームの次の内容を確認します。

return (i >> std::boolalpha >> t && i.peek() == EOF);

あなたのバージョンは整数に対しても機能しません。次の入力を検討してください123 45。ストリームにまだいくつかの文字が残っている場合でも、123 が読み取られて true が報告されます。

于 2012-11-07T17:01:08.127 に答える
3

jrokの答えを拡張すると、少なくともこの場合は、とi.get()同じくらい簡単に 使用できます。i.peek()(どちらか一方を優先する理由があるかどうかはわかりません。)

また、空白は区切り文字にすぎないという規則に従って、末尾を確認する前に空白を抽出することをお勧めします。何かのようなもの:

return i >> std::ws && i.get() == std::istream::traits_type::eof();

の古い実装のいくつかstd::wsはバグがあり、ストリームをエラー状態にしていました。その場合、テストを逆にして、次のようなことを行う必要があります。

return !(i >> std::ws) || i.get() == std::istream::traits_type::eof();

または、条件の前を読んで、std::wsに一意に依存しi.get()ます。

(バギーがまだ問題であるかどうかはわかりませんstd::ws。以前に機能するバージョンを開発し、それを使い続けました。)

于 2012-11-07T17:13:58.400 に答える
3

標準ライブラリの多くの実装ではeof、最後を超えて読み込もうとした後にのみ設定されます。次のようにして、コードでそれを確認できます。

char _;
if (i && !(i >> _)) { // i is in a valid state, but
                      // reading a single extra char fails
于 2012-11-07T17:01:25.893 に答える
2

まったく異なるアプローチを提供したいと思います。入力文字列を取得し、それを自分でトークン化し、 を使用して個々のフィールドを変換します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"`

i5 (OK) にj設定され、9 (??) に設定され、 6.0 (= 0.6e x+01) に設定され、 y550 (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。上記のコード例の変更は、読者への演習として残されています :-)

于 2015-04-10T14:31:16.233 に答える