2012-10-06 90 views
1

我有一個包含整數和特殊含義字符'#'的輸入流。它看起來如下: ... 12 18 16 # 22 24 26 15 # 17 # 32 35 33 ... 令牌由空格分隔。 '#'的位置沒有模式。根據類型標記字符串流

我試圖來標記輸入流是這樣的:

int value; 
std::ifstream input("data"); 
if (input.good()) { 
    string line; 
    while(getline(data, line) != EOF) { 
    if (!line.empty()) { 
     sstream ss(line); 
     while (ss >> value) { 
     //process value ... 

     } 
    } 
    } 
} 

這段代碼的問題是,處理遇到的第一個「#」時停止。

我能想到的唯一解決方案是將每個單獨的標記提取爲一個字符串(不是'#')並使用atoi()函數將字符串轉換爲整數。但是,由於大多數令牌都是整數,所以效率很低。在令牌上調用atoi()會帶來很大的開銷。

有沒有一種方法可以根據其類型解析個人令牌?即整數,解析爲整數,而'#',跳過它。謝謝!

+1

使用函數getline兩次acccepable?如果是這樣,使用'getline(data,line,'#');'fisrt。 – andre

+0

@ahenderson我沒有明白你的觀點。 getline的一個函數簽名是:istream&getline(istream&is,string&str,char delim);通過將'#'作爲第三個參數傳遞給getline,getline()將以'#'作爲分隔符。 – itnovice

+0

我發佈了一個答案。 – andre

回答

2

一種可能性是顯式跳過空格(ss >> std::ws),然後使用ss.peek()來確定是否跟隨#。如果是,則使用ss.get()來讀取並繼續,否則使用ss >> value讀取該值。

如果#的位置不重要,也可以在將其初始化爲stringstream之前從行中刪除所有'#'

+0

謝謝。您的解決方案總是展望未來,'#'使用ss.get()來消耗它並繼續前進。如果'#'可以是其他非數字字符,我們可以有更通用的解決方案嗎? – itnovice

+0

@itnovice:如果你的數字都是正整數(即只包含數字「0」到「9」),你可以將它傳遞給'isdigit'。如果是數字,則可以繼續閱讀該值,否則您知道它是非數字字符。或者,您可以使用測試'next''0'|| next>'9'(其中'next'包含'peek'的結果)來標識非數字。 – celtschk

0

就個人而言,如果你的分隔符是總是不管接下來是什麼,我建議你只需要輸入字符串並從那裏解析。這樣,你可以拿起字符串,看看它是一個數字還是#和什麼。

1

也許可以讀取所有值的std :: string,然後檢查它是否是「#」或沒有(如果沒有 - 轉換爲int)

0

我認爲你應該重新審視你的前提是「呼喚atoi()上的令牌引入了大量的開銷 - 「

std::cin >> val沒有什麼神奇。在引擎蓋下,它最終召喚(與atoi非常相似)。

如果您的代幣很大,創建std::string可能會有一些開銷,但正如您所說,絕大多數是數字(其餘都是#),所以它們大多應該是短的。

+0

我不知道std :: cin >> val最終調用了一些類似於atoi()的函數。所以我的假設是錯誤的。感謝您指出了這一點。 – itnovice

1
int value; 
std::ifstream input("data"); 
if (input.good()) { 
    string line; 
    std::sstream ss(std::stringstream::in | std::stringstream::out); 
    std::sstream ss2(std::stringstream::in | std::stringstream::out); 
    while(getline(data, line, '#') { 
     ss << line; 
     while(getline(ss, line, ' ') { 
      ss2 << line; 
      ss2 >> value 
      //process values ... 
      ss2.str(""); 
     } 
     ss.str(""); 
    } 
} 

在這裏,我們首先在第一拆分令牌「#」行while循環然後在第二個while循環,我們通過「」分割線。

+0

我在這臺機器上沒有C++編譯器,代碼沒有測試。所以把它作爲它應該如何工作的大綱。 – andre

+0

我明白你的觀點。這是一個聰明的解決方案。謝謝 – itnovice

+0

@itnovice小錯誤我有'ss >>值'應該是ss2 >>值 – andre

2

通常不值得對測試好()

if (input.good()) { 

除非你的下一個操作是生成錯誤消息或異常。如果不好,所有進一步的操作都會失敗。

不要針對EOF進行測試。

while(getline(data,line)!= EOF){

std :: getline()的結果不是整數。它是對輸入流的引用。輸入流可轉換爲布爾類似的對象,可用於布爾上下文中(如whileif等..)。所以你想做什麼:

while(getline(data, line)) { 

我不知道我會讀一行。你可以只讀一個詞(因爲輸入是空格分隔的)。在串

std::string word; 
while(data >> word) { // reads one space separated word 

使用>>運算符現在可以測試這個詞,看它是否是你的特殊字符:

if (word[0] == "#") 

如果沒有的話轉換成一個數字。

這是我會做什麼:

// define a class that will read either value from a stream 
class MyValue 
{ 
    public: 
    bool isSpec() const {return isSpecial;} 
    int value() const {return intValue;} 

    friend std::istream& operator>>(std::istream& stream, MyValue& data) 
    { 
     std::string item; 
     stream >> item; 
     if (item[0] == '#') { 
      data.isSpecial = true; 
     } else 
     { data.isSpecial = false; 
      data.intValue = atoi(&item[0]); 
     } 
     return stream; 
    } 
    private: 
    bool isSpecial; 
    int intValue; 
}; 

// Now your loop becomes: 
MyValue val; 
while(file >> val) 
{ 
    if (val.isSpec()) { /* Special processing */ } 
    else    { /* We have an integer */ } 
} 
+0

您的解決方案是非常好的。謝謝! – itnovice