2012-01-21 42 views
7

我的代碼從文本文件Input_File_Name中讀取無符號整型變量。如何使用ifstream正確讀取文件中的無符號整型變量?

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

這只是'運營商>>'如何工作;它會提取'33'然後停止,'abcd'停留在流中,以便下次調用'operator >>'。你可以改爲將'33abcd4'讀入一個字符串,然後檢查它中的非數字字符。另外,如果你有最近支持C++ 11的編譯器,檢查標準庫是否提供'std :: stoull'。 – jrok

回答

4

如果流可以讀取任何整數值,則正常提取整數值會成功。也就是說,如果至少有一個數字可選地跟隨任何東西,則讀取整數成功。正常的提取操作不要嘗試閱讀更多,特別是他們不試圖找到下一個空白。

從它的聲音中,你要確保數字後面有一個空格,如果沒有,就會失敗。我可以想到兩種不同的方法來做到這一點:

  1. 創建一個簡單的操縱器,它檢查在一個空白字符上的流。但是,這意味着你會使用類似in >> value >> is_space的東西來讀取你的值。
  2. 創建自定義std::num_get<char>構面,將其安裝到std::localeimbue()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:爲了完成其他整型,必須重寫更多的函數。所以,這裏是爲std::num_get<char>方面的替代品:

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::ios_base::failbit錯誤結果err

什麼仍然是建立在信息流中std::locale使用這種特殊的面和鉤新std::locale到流:

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

std::locale S和它的方面是內部引用計數,即你不應該不會跟蹤指向該方面的指針,也不需要保留std::locale。如果對imbue()創建std::locale或您想要在任何地方使用此修改後的邏輯似乎很麻煩,則可以設置用於初始化任何新創建的流的全局std::locale以使用自定義std::num_get<char>構面。

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"; 
    } 
} 

如果你想這對於其他類型的工作太,然後實現以同樣的方式如下:

iter_type do_get(iter_type, iter_type, ios_base&, ios_base::iostate&, T& v) const 

其中T是一個boollonglong longunsigned shortunsigned longunsigned long longfloatdoublelong doublevoid*