2011-08-16 46 views
0

我需要實現一種機制,可以使用文本源初始化我的自定義類的向量,其中源的每一行代表我的類的一個實例。爲了達到這個目標,我爲我的班級實施了operator >>,並實施了stringstream。當我閱讀源代碼時,我會一行一行地獲取我原始源的子流,然後每次解析子流。這對我有三個好處。首先,通過這種方式,我可以確保文本源的一行代碼恰好代表我的課程的一個實例。其次,由於解析後的其餘部分被忽略,我可以安全地在我的文本源的任何行中添加任何註釋,這肯定會被解析器忽略。第三,我不需要在我的原始源代碼中提到向量的長度,因爲我第一次得到解析錯誤(我檢查流的failbad位以確認這一點)我知道向量聲明結束了。有效獲取stringstream的子流

剖析行由行,我用下面的代碼:

std::stringstream  fullStream; 
std::stringstream  lineStream; 
std::string    str; 
bool     isValid; 
myClass     newInstance; 
std::vector <myClass> result; 

// Fill fullStream from external source (codepart omitted) 
isValid = true; 
while (isValid && ! fullStream.eof ()) { 
    std::getline (fullStream, str); 
    lineStream.clear (); 
    lineStream.str (str); 
    lineStream >> newInstance; 
    isValid = ! lineStream.fail (); 
    if (isValid) { 
     result.push_back (newInstance); 
    } 
} 

雖然這個代碼工作正常,我不知道是否有更好的方法來達到同樣的效果。特別是,如果從fullStreamlineStream有更高效的方法提取行。

感謝,
亞當

+0

僅僅用迭代器掃描輸入字符串,而不是使用兩個(如果不是三個不同的流)會不會簡單得多?在你的地方,我將你的提取操作符的邏輯移動到一個簡單的函數,它將一個字符串迭代器對作爲參數並擺脫流。我相信你的代碼會更清晰。 –

+0

我正在使用'operator >>',因爲這樣所有的whitespace-management和string-> float轉換都是自動處理的。我不知道如何在不使用'stringstream'的情況下實現這個功能。 –

+0

我明白你的觀點。 –

回答

2

一個明顯的替代方法是讓你的operator>>做行由行閱讀本身,所以你不必做外部:

class MyClass { 
    // some sort of data to demonstrate the idea: 
    int x; 
    std::string y; 

    friend std::istream &operator>>(std::istream &is, MyClass &m) { 
     std::string temp; 
     std::getline(is, temp); 
     std::istringstream buffer(temp); 
     buffer >> m.x >> m.y; 
     return is; 
    } 
}; 

隨着從文件中讀取數據的代碼變得更直接:

std::copy(std::istream_iterator<MyClass>(fullStream), 
      std::istream_iterator<MyClass>(), 
      std::back_inserter(result)); 

編輯:如果你不「T希望建立面向行的直接讀入operator>>MyClass,另一種可能是使用代理類:當你想要做的面向線路的閱讀

class LineReader { 
    MyClass object; 
public: 
    operator MyClass() { return object; } 

    friend std::istream &operator>>(std::istream &is, LineReader &d) { 
     std::string line; 
     std::getline(is, line); 
     std::istringstream buffer(line); 
     buffer >> d; // delegate to the object's own stream-oriented reader. 
    } 
}; 

然後,您閱讀對象代理類,但店原班對象:

std::vector<MyClass>((std::istream_iterator<LineReader>(some_stream)), 
         std::istream_iterator<LineReader>()); 

但是,當/如果你想閱讀的對象,而不是對象的行流,您使用對象自己operator>>直接:

std::vector<MyClass>((std::istream_iterator<MyClass>(stream), 
         std::istream_iterator<MyClass>()); 
+0

我真的不喜歡把逐行解析部分移到'operator >>'函數的想法,因爲這會阻止我解析沒有換行符的流(我可能需要在我的項目的後期狀態)。問題是我的MyClass類實際上是一個N維點的實現,我在項目的幾個部分使用了它,出於不同的目的,我可能需要從一行解析多個點。 –

+0

@Jerry,第一個例子應該讀取'if(std :: getline(is,temp))'。切勿使用未經驗證的輸入! – sbi

+0

@sbi:是的,相當真實。我發佈的大部分內容缺乏(甚至接近)足夠的無效輸入檢查 - 或者大多數其他錯誤檢查。儘管異常處理有幫助,但真正的代碼仍然常常以真正的意圖結束,幾乎隱藏在一大堆錯誤檢查中(當處理用戶輸入時尤其如此)。 –

3

首先,如果代碼起作用,那真的只是偶然。處理這種情況的慣用 方法是:

std::string line; 
while (std::getline(fullStream, line)) { 
    std::istringstream lineStream(line); 
    lineStream >> newInstance; 
    if (lineStream) { 
     result.push_back(newInstance); 
    } else { 
     fullStream.setstate(std::ios_base::failbit); 
    } 
} 

檢查eof()之前讀很少有用,並在使用前沒有檢查 結果你getline的幾乎可以肯定是一個錯誤。 嘗試重複使用stringstream比 更簡單並且容易出錯,只需創建一個新的;有各種各樣的可能或可能不需要重置的狀態。流有一個記住錯誤 狀態的機制,所以你可能想用這個。(如果您想在錯誤發生後繼續使用 fullStream來解決其他問題,則問題更復雜,因爲您已經提取了失敗的行,並且您的 無法恢復。)如果您只有閱讀,你應該使用 std::istringstream,而不是std::stringstream(它有很多 額外的行李);一般來說,使用雙向流的非常非常罕見。