2012-01-21 12 views
7

私のコードは、テキストファイルInput_File_Nameから符号なしint変数を読み込みます。ifstreamを使用して、unsigned int変数をファイルから正しく読み取る方法は?

unsigned int Column_Count; //Cols 
unsigned int Row_Count;//Rows 

try { 
    ifstream input_stream; 
    input_stream.open(Input_File_Name,ios_base::in); 
    if (input_stream) { 
     //if file is opened 
     input_stream.exceptions(ios::badbit | ios::failbit); 
     input_stream>>Row_Count; 
     input_stream>>Column_Count; 


    } else { 
     throw std::ios::failure("Can't open input file"); 
     //cout << "Error: Can't open input file" << endl; 
    } 

} catch (const ios::failure& error) { 
    cout << "Oh No!!" << error.what() << endl;   
} catch (const exception& error) { 
    cout << error.what() <<"Oh No!!" << endl; 
} catch (...) { 
    cout << "Unknown exception" << endl; 
} 

優れた動作をします。 しかし、私はそれは、このような方法で動作し、誤ったデータ

33abcd4 567fg8 

でテキストファイルを埋めるとき:

input_stream>>Row_Count; //Row_Count = 33; 
input_stream>>Column_Count; // throws an ios::failure exception 

しないのはなぜこのラインinput_stream>>Row_Count;スロー例外? 私が理解したように、input_streamは任意の非数値シンボルを区切り文字と見なし、次のステップでは "abcd"を読み込もうとします。そうですか? "33abcd4"を読み取っているときに、このコード行input_stream>>Row_Count;からios::failure例外をスローするためにスペース記号を区切り記号として設定するにはどうすればよいですか?

+1

これは、 'operator >>'がどのように動作するかです。 '33'を抽出して停止すると、 'abcd'は 'operator'への次の呼び出しのためにストリームにとどまります。代わりに文字列に '33abcd4'を読み込み、その中の非数字文字をチェックすることができます。また、C++ 11をサポートする最近のコンパイラをお持ちの場合、標準ライブラリが 'std :: stoull'を提供しているかどうかを確認してください。 – jrok

答えて

4

ストリームが任意の整数値を読み取ることができる場合、通常の整数値の抽出は成功します。つまり、任意に1つ以上の数字の後に1つ以上の数字が続く場合、整数の読み取りは成功します。通常の抽出操作ではありません。特に、次の空白を見つけようとしません。

その音から、番号の後に空白があることを確認し、そうでない場合は失敗します。

  1. ストリームが空白文字であることを確認する簡単なマニピュレータを作成します。しかし、これは、in >> value >> is_spaceのようなものを使って値を読み取ることを意味します。
  2. カスタムstd::num_get<char>ファセットを作成し、std::localeに、imbue()std::localeをストリームにインストールします。これはもう少し複雑ですが、整数の読み方を変更する必要はありません。このようなマニピュレータを作成

はかなり簡単です:

std::istream& is_space(std::istream& in) 
{ 
    if (!std::isspace(in.peek())) 
    { 
     in.setstate(std::ios_base::failbit); 
    } 
    return in; 
} 

、数字が読み込まれる方法を変更することは、より興味深く、私はちょうどほとんどの人がかなりいる標準ライブラリのクラスの数を命名していた疑いがあります知らないだから、これについての例もすぐにタイプしましょう。 std::num_get<char>ファセットをunsigned intとしか扱わないように変更します。これは、他の整数型に対して行うには、より多くの関数をオーバーライドする必要があります。このすべてが行う

class num_get: 
    public std::num_get<char> 
{ 
    iter_type do_get(iter_type it, iter_type end, 
        std::ios_base& ios, std::ios_base::iostate& err, 
        unsigned int& value) const 
    { 
     it = std::num_get<char>::do_get(it, end, ios, err, value); 
     if (it != end && !isspace(static_cast<unsigned char>(*it))) 
     { 
      err |= std::ios_base::failbit; 
     } 
     return it; 
    } 
}; 

std::num_get<char>からクラスを派生し、その仮想関数のいずれかをオーバーライドすることです。だから、ここstd::num_get<char>小面のための交換があります。この関数の実装はかなり単純です:基本クラスに委譲して値を読み取ることから始めます(これまでの仮想関数は私が以前と同じようにプライベートではなく保護したいと考えていましたが、これは全く異なる議論です) 。これが成功したかどうかに関係なく、エラーが発生していない場合は、errにエラー状態が設定されています。使用可能な別の文字があるかどうかを調べ、空白かどうかをチェックし、空白でない場合はstd::ios_base::failbitをエラー結果errstd::localeにこの特定のファセットを使用するようにストリームを設定し、ストリームに新しいstd::localeをフックするためには何まま

std::locale loc(std::locale(), new num_get); 
in.imbue(loc); 

std::locale sおよびそのファセットは、内部での参照がカウントされ、あなたはshouldnすなわちファセットへのポインタを追跡しておらず、std::localeの周りに保持する必要はありません。 imbue()作成したstd::localeに煩わしいと思われる場合や、この変更されたロジックをどこでも使用したい場合は、カスタムstd::num_get<char>ファセットを使用するように新しく作成されたストリームを初期化するために使用するグローバルstd::localeを設定できます。

2

あなたは、このようにそれを行うことができます。

#include <iostream> 
#include <locale> 

class my_num_get : public std::num_get<char> { 
protected: 
    iter_type do_get(iter_type in, iter_type end, std::ios_base& str, std::ios_base::iostate& err, unsigned int& v) const 
    { 
     in = std::num_get<char>::do_get(in, end, str, err, v); 
     if(in != end && !std::isspace(*in, str.getloc())) 
      err |= std::ios_base::failbit; 
     return in; 
    } 
}; 

int main() { 
    using namespace std; 
    cin.imbue(std::locale(cin.getloc(), new my_num_get)); 
    cin.exceptions(ios_base::badbit | ios_base::failbit); 
    try { 
     unsigned int x; 
     cin >> x; 
    } catch(const std::exception &e) { 
     cerr << e.what() << "\n"; 
    } 
} 

あなたは、これはあまりにも他のタイプのために仕事をしたい場合は、同じように次のように実装します。

Tのいずれかである
iter_type do_get(iter_type, iter_type, ios_base&, ios_base::iostate&, T& v) const 

boollonglong longunsigned shortunsigned longunsigned long longfloatdoublelong doublevoid*

関連する問題