web-dev-qa-db-ja.com

stringstream operator >>が不正な型を解析してスキップするかどうかをテストする方法

stringstreamを使用して複数のタイプの行を解析する方法について説明することに興味があります。私は次の行を見ることから始めます:

"2.832 1.3067 nana 1.678"

ここで、複数のstringsdoublesがある長い行があると仮定します。これを解決する明白な方法は、文字列をトークン化してから、各文字列の変換を確認することです。この2番目のステップをスキップし、stringstreamを直接使用して数字のみを検索することに興味があります。

これにアプローチする良い方法は、文字列を読み、failbitが設定されているかどうかを確認することであると考えました。

次のコードがあるとします。

string a("2.832 1.3067 nana 1.678");

 stringstream parser;
 parser.str(a);

 for (int i = 0; i < 4; ++i)
 {
     double b;
     parser >> b;
     if (parser.fail())
     {
         std::cout << "Failed!" << std::endl;
         parser.clear();
     }
     std::cout << b << std::endl;
 }

以下を印刷します。

2.832
1.3067
Failed!
0
Failed!
0

文字列の解析に失敗しても驚くことではありませんが、failbitのクリアと次の数値の解析に失敗するなど、内部で何が起こっているのでしょうか?

28

次のコードは、bad Wordをスキップして有効なdouble値を収集するのに適しています

_istringstream iss("2.832 1.3067 nana 1.678");
double num = 0;
while(iss >> num || !iss.eof()) {
    if(iss.fail()) {
        iss.clear();
        string dummy;
        iss >> dummy;
        continue;
    }
    cout << num << endl;
}
_

完全に動作するサンプル です。


サンプルはほぼ正しく、間違った形式を検出した後、ストリームから無効な入力フィールドを消費するだけでした

_ if (parser.fail()) {
     std::cout << "Failed!" << std::endl;
     parser.clear();
     string dummy;
     parser >> dummy;
 }
_

あなたの場合、最後の繰り返しのために、抽出は_"nana"_から再度読み込もうとします。したがって、出力の最後の2行です。

また、iostream::fail()に関するトリックと、最初のサンプルでiostream::eof()を実際にテストする方法にも注意してください。 よく知られているQ&Aがあります 、なぜEOFのループ条件としての単純なテストが間違っていると考えられます。ただし、無効な入力フィールドをスキップ/無視する方法については説明されていません(要求もされていません)。

20

Πάνταῥεῖの答えにはわずかな違いがほとんどありません。負の数表現など、同様に-私見-少し読みやすい。

#include <iostream>
#include <sstream>
#include <string>

int main()
{
    std::istringstream iss("2.832 1.3067 nana1.678 x-1E2 xxx.05 meh.ugh");
    double num = 0;
    for (; iss; )
        if (iss >> num)
            std::cout << num << '\n';
        else if (!iss.eof())
        {
            iss.clear();
            iss.ignore(1);
        }
}

出力:

2.832
1.3067
1.678
-100
0.05

(実行中の こちら を参照)

3
Tony Delroy

簡潔さを好む場合-数値が正常に解析され、数値が解析されない場合にのみ_&&_を使用してcoutを実行する別のオプションがありますclear()無視される文字を読み取る前の条件内のストリームエラー状態...

_#include <iostream>
#include <sstream>
#include <string>

int main()
{
    std::istringstream iss("2.832 1.3067 nana1.678 x-1E2 xxx.05 meh.ugh");
    double num = 0;
    char ignored;
    while (iss >> num && std::cout << num << '\n' ||
           (iss.clear(), iss) >> ignored)
        ;
}
_

http://ideone.com/Wvtvf

2
Tony Delroy

std::istringstream::eof()を使用して、次のようにvalidateに入力できます。

_#include <string>
#include <sstream>
#include <iostream>

// remove white-space from each end of a std::string
inline std::string& trim(std::string& s, const char* t = " \t")
{
    s.erase(s.find_last_not_of(t) + 1);
    s.erase(0, s.find_first_not_of(t));
    return s;
}

// serial input
std::istringstream in1(R"~(
 2.34 3 3.f 3.d .75 0 wibble 
)~");

// line input
std::istringstream in2(R"~(
2.34
 3

3.f
3.d
.75
0
wibble 
)~");

int main()
{
    std::string input;

    // NOTE: This technique will not work if input is empty
    // or contains only white-space characters. Therefore
    // it is safe to use after a conditional extraction
    // operation >> but it is not reliable after std::getline()
    // without further checks.

    while(in1 >> input)
    {
        // input will not be empty and will not contain white-space.
        double d;
        if((std::istringstream(input) >> d >> std::ws).eof())
        {
            // d is a valid double
            std::cout << "d1: " << d << '\n';
        }
    }

    std::cout << '\n';

    while(std::getline(in2, input))
    {
        // eliminate blank lines and lines
        // containing only white-space (trim())
        if(trim(input).empty())
            continue;

        // NOW this is safe to use

        double d;
        if((std::istringstream(input) >> d >> std::ws).eof())
        {
            // d is a valid double
            std::cout << "d2: " << d << '\n';
        }
    }
}
_

これは、eof()チェックによってonlyが入力され、_12d4_のようなゴミではないことが確認されるためです。

1
Galik