2013-07-23 12 views
7

我試圖使用字符串流來分割字符串:字符串流複製硬道理

#include <fstream> 
#include <iostream> 
#include <sstream> 
#include <string> 

using namespace std; 

int main(){ 
    ifstream fp; 
    string name; 

    fp.open("in.txt"); 
    while (fp){ 
     string line; 
     getline(fp, line); 
     cout << line << endl; 
     istringstream line_stream(line); 

     while (line_stream){ 
      line_stream >> name; 
      cout << name << " "; 
     } 
    } 

    return 0; 
} 

這裏的in.txt:

cat bat rat sat 

這裏是我得到的輸出:

cat bat rat sat 
cat bat rat sat sat 

getline()函數檢索的行是正確的,但在分裂的過程中,我得到了最後一個詞兩次。我不確定爲什麼會發生這種情況。

+7

問題是'while(line_stream)'。使用'while(line_stream >> name){...}' –

+0

@gx_不會將它添加爲答案嗎? – triclosan

+0

@triclosan因爲這不是一個完整的答案。 (並沒有解釋_why_。) –

回答

7

您正在使用getline的結果,但未檢查其是否成功地使用了 。這是第一個錯誤(並且可能導致 對您顯示的代碼有額外的空行)。同樣,您使用 的結果爲line_stream >> name而不檢查其是否成功 ;在這種情況下(因爲name每次都不是新構建的 ),您可能會以前面讀取的 值(但在這兩種情況下,該字符串的內容未指定爲 )結束。

你必須從不使用輸入的結果沒有先測試 是否成功。這樣做(但 肯定不是唯一的方法)的最常用的方法是做輸入迴路的條件 :

while (std::getline(fp, line)) ... 

while (line_stream >> name) ... 

如果您仍然希望爲了限制變量的範圍爲 循環,您必須編寫:

while (fp) { 
    std::string line; 
    if (std::getline(fp, line)) { 
     // rest of loop 
    } 
} 

如果您有(可以理解),對在條件修正全球 狀態的東西,你必須寫:

std::getline(fp, line); 
while (fp) { 
    // ... 
    std::getline(fp, line); 
} 

雖然我認爲有贊成這一強有力的論據, 的while (std::getline(fp, line))成語是無處不在,到 其他任何事情都會導致讀者想知道爲什麼 。

1

不要說:

while (fp){ 
    string line; 
    getline(fp, line); 
    ... 

你應該說:

string line; 
while(getline(fp, line)) { ... 

這是因爲當FP進入EOF狀態,函數getline失敗(和套FP到EOF狀態)。您不檢查getline的結果,以便在最後一步使用先前讀取的值。

1

問題在於Loop。一旦它從文件中讀取最後一個單詞並再次進入循環,那麼該條件不會失敗,並且它會再次循環。

變化都while循環來

while(getline(fp, line)) 

&

while (line_stream >> name) 

這將避免重複在此情況下,最後name將不會被處理兩次。目前當fp接近eof時,getline失敗。 getline未被檢查並且使用先前讀取的值,而不檢查其是否成功。

1

除了所建議的修改通過檢查getline結果檢測文件的末尾,相同的原則應適用於該stringstream,所以:

while (line_stream){ 

應該是:

while (line_stream >> name) { 

這樣,當「沒有剩下任何其他名字」時,你不會得到最後的name兩次處理。

+0

它的工作原理,但遺憾的是我仍然無法理解爲什麼?,我的意思是當我做line_stream >>名字時,line_stream不應該變成空的最後一個詞,因此當它回到循環時,現在是假的。 –

+1

上次讀取失敗時,設置流的「失敗」位(不管是字符串,文件還是其他)。因此,如果我們有一個包含'Joe!'(其中'!'表示數據結尾)的流,那麼'失敗'將在名稱「Joe」被讀取後發生。但是由於測試(在原始代碼中)發生在數據結束與名稱「Joe」之間的空間已被讀取之前,流尚未處於「失敗」狀態。所以你最終會處理「Joe」兩次 - 因爲失敗的讀取不會改變'name',但是沒有檢查是否成功。 –

+0

@MatsPetersson即使沒有額外的空間:「Joe」的讀取成功。它設置了'eofbit',但這並不意味着失敗,測試流仍然可以工作。只有當他試圖用'eofbit'設置輸入失敗時再讀一個單詞時。 –