アプリケーションへの入力データは決して信頼できないため、エラー チェックを追加して、提供されたデータが実際に有効であることを確認することが重要です (そうしないと、解析中にアプリケーションの結果にエラーが発生する可能性があります)。
このようなエラーを処理する"C++ の方法"は、データの解析を担当する関数で問題が発生したときに例外をスローすることです。
この関数の呼び出し元は、try-catch-blockで呼び出しをラップして、表示される可能性のあるエラーをキャッチします。
ユーザー定義型で..
データのペアを保持するための独自の型を定義すると、コードの可読性が大幅に向上します。以下の実装からの出力と、この投稿の後半にある出力は同じです。
#include <iostream>
#include <string>
#include <sstream>
#include <stdexcept>
struct Pair {
Pair (int a, int b)
: value1 (a), value2 (b)
{}
static Pair read_from (std::istream& s) {
int value1, value2;
if ((s >> std::ws).peek () != '(' || !s.ignore () || !(s >> value1))
throw std::runtime_error ("unexpected tokens; expected -> (, <value1>");
if ((s >> std::ws).peek () != ',' || !s.ignore () || !(s >> value2))
throw std::runtime_error ("unexpected tokens; expected -> , <value2>");
if ((s >> std::ws).peek () != ')' || !s.ignore ())
throw std::runtime_error ("unexpected token;expected -> )");
return Pair (value1,value2);
}
int value1, value2;
};
プログラマーが上記について理解するのが難しいかもしれないことに気付いた 1 つのことは、s >> std::ws
;の使用です。使用可能な空白を消費するために使用されるため、使用.peek
可能な次の非空白文字を取得するために使用できます。
read_from
代わりに静的関数を実装した理由は、後者ostream& operator>>(ostream&, Pair&)
では、ストリームから読み取る前にオブジェクトを作成する必要があるためです。これは場合によっては望ましくありません。
void
parse_data () {
std::string line;
while (std::getline (std::cin, line)) {
std::istringstream iss (line);
int N, M;
if (!(iss >> N >> M))
throw "unable to read N or M";
else
std::cerr << "N = " << N << ", M = " << M << "\n";
for (int i =0; i < M; ++i) {
Pair data = Pair::read_from (iss);
std::cerr << "\tvalue1 = " << data.value1 << ", ";
std::cerr << "\tvalue2 = " << data.value2 << "\n";
}
}
}
通常、非 const 変数に大文字のみで名前を付けることはお勧めしませんが、どの変数に入力の説明と同じ名前を使用するかをより明確にするために使用します。
int
main (int argc, char *argv[])
{
try {
parse_data ();
} catch (std::exception& e) {
std::cerr << e.what () << "\n";
}
}
ユーザー定義型を使用しない場合
データを解析し、エラーをチェックする簡単な方法は、次のようなものを使用することですが、ユーザー定義オブジェクトと演算子のオーバーロードを使用すると大幅に改善される可能性があります。
- std::getlineを使用して各行を読み取る
- 読み取り行でn std::istringstream iss (行)を構築します
- iss >> N >> Mを使用して 2 つの int を読み取ろうとする
- std::string s1* とiss >> s1を使用して、 M個の 「単語」を読み取ります。
- s1を初期化子として使用してstd::istringstream inner_issを構築します
- 利用可能な次の文字が
(
&& この文字を無視することを確認してください
- 整数を読み取る
- 利用可能な次の文字が
,
&& この文字を無視することを確認してください
- 整数を読み取る
- 利用可能な次の文字が
)
&& この文字を無視することを確認してください
ステップ 4 の後で stringstream が空でない場合、またはiss.good ()がステップ間のどこかで false を返す場合は、読み取ったデータの構文エラーです。
実装例
ソースは以下のリンクから見つけることができます (コードはスペースを節約するために他の場所に配置されています)。
N = 0, M = 0
N = 2, M = 1
value1 = 0, value2 = 1
N = 2, M = 0
N = 5, M = 8
value1 = 0, value2 = 1
value1 = 1, value2 = 3
value1 = 2, value2 = 3
value1 = 0, value2 = 2
value1 = 0, value2 = 1
value1 = 2, value2 = 3
value1 = 2, value2 = 4
value1 = 2, value2 = 4