3

最近、標準入力で発生するすべての整数を、1 行に 1 つの整数で合計する単純なプログラムが必要でした。入力には、単一のマイナス ('-') 記号を含む未設定の行と、ジャンク文字 (無視される) を含む行が含まれていました。

これは些細なプログラムになるだろうと私は信じていました。しかし、結局のところ、単一のマイナス記号は他の悪い入力として動作しませんでした。通常の非整数入力では、失敗フラグが設定され、不正な入力が入力バッファに残ります。ただし、マイナス (またはプラス) 記号が 1 つあると、失敗フラグが設定されますが、+/- 記号が削除されるため、プログラムは次の (有効な) 整数をスキップします (合計が正しくありません)。

動作を分析するために、小さなテスト プログラム (以下) を作成しました。+/- 記号を使用した上記の動作は、バグまたは機能ですか?

#include <iostream>

using namespace std;

int main()
{
  string s;
  int n;

  while (true)
  {
    n = -4711;
    cin >> n;

    cerr << (cin.bad()  ? "ERROR: badbit is set\n" : "");
    cerr << (cin.fail() ? "ERROR: failbit is set\n" : "");
    cerr << (cin.eof()  ? "ERROR: eofbit is set\n" : "");

    if ( cin.bad() || cin.eof() )
      break;

    if ( cin.fail() )
    {
      cin.clear();
      cin >> s;
      cerr << "ERROR: ignored string '" << s
           << "' (integer is '" << n << "')" << endl;
    }
    else
    {
      cout << "OK: read integer '" << n << "'" << endl;
    }
  }
  return 0;
}

プログラムの実行 (入力: "1 asdf 2 - 3 + 4 qwer 5"):

~ $ ./a.out
1 asdf 2 - 3 + 4 qwer 5 
OK: read integer '1'
ERROR: failbit is set
ERROR: ignored string 'asdf' (integer is '0')
OK: read integer '2'
ERROR: failbit is set
ERROR: ignored string '3' (integer is '0')
ERROR: failbit is set
ERROR: ignored string '4' (integer is '0')
ERROR: failbit is set
ERROR: ignored string 'qwer' (integer is '0')
OK: read integer '5'

(代わりに文字列を読み取り、C++ 11 の stoi を例外付きで使用して不正な入力を識別することで、元の問題を解決しました。)

編集:誰かが私の元の問題の解決策に興味がある場合:

#include <iostream>
#include <string>

using namespace std;

int main()
{
  int sum = 0;
  string n;

  while ( cin >> n )
  {
    try {
      sum += stoi(n);
      cout << n << endl;
    }
    catch (exception& e)
    {
      cerr << e.what() << " ERROR: '" << n << "' is not a number." << endl;
    }
  }
  cout << sum << endl;

  return 0;
}
4

2 に答える 2

2

特徴です。cin「-」の後のスペースを読み取るまで、入力にエラーがあることはわかりません。iostream を使用して複数の文字を戻す移植可能な方法はなく、cin をシークする移植可能な方法もありません。そのため、スタックしており、「-」を読み取ったままにしておく必要があります。

データを読み取るときに、自分で解析したほうがよいというポイントがあります。すべてのデータを文字列に読み取り、それらの文字列を自分で解析して、何が本物で何がジャンクかを判断します。そうすれば、iostream が行うことと戦う代わりに、完全に制御できます。

于 2013-03-16T19:09:01.257 に答える
1

私はそれを次のようにします:

#include <locale>
#include <sstream>
#include <vector>
#include <iostream>
#include <iterator>
#include <algorithm>

struct number_only: std::ctype<char> { 
    number_only() : std::ctype<char>(get_table()) {} 

    static mask const *get_table() { 
        static std::vector<mask> rc(table_size, space);

        std::fill_n(&rc['0'], 10, digit);
        return &rc[0]; 
    } 
};

int main() { 
    std::string input("1 asdf 2 - 3 + 4 qwer 5 ");
    std::istringstream x(input);

    // use our ctype facet:
    x.imbue(std::locale(std::locale(), new number_only));

    // initialize vector from the numbers in the file:
    std::vector<int> numbers((std::istream_iterator<int>(x)), 
                                std::istream_iterator<int>());

    // display what we read:
    std::copy(numbers.begin(), numbers.end(), 
        std::ostream_iterator<int>(std::cout, "\n"));

    return 0;
}
于 2013-03-16T19:11:11.127 に答える