2015-11-02 157 views
-2

我收到來自的strtok和strtrok_r功能意外的行爲使用的strtok/strtok_r:在C++ while循環

queue<string> tks; 
char line[1024]; 
char *savePtr = 0; 

while(true) 
{ 
    //get input from user store in line 

    tks.push(strtok_r(line, " \n", &savePtr)); //initial push only works right during first loop 

    char *p = nullptr; 
    for (...) 
    { 
     p = strtok_r(NULL, " \n", &savePtr); 
     if (p == NULL) 
     { 
      break; 
     } 
     tks.push(p); 
    } 
    delete p; 
    savePtr = NULL; 

    //do stuff, clear out tks before looping again 

} 

我使用strtok嘗試,實現了第二循環的過程中,最初的推沒有發生。我嘗試使用可重入版本strtok_r來控制第二個循環期間保存的指針指向的內容,方法是在再次循環前確保它爲空。

tks是在第一時間通過循環唯一正確填充 - 後續循環給出不同的結果取決於line

長度缺少什麼我在這裏?

+1

[mcve]或者它沒有發生。 –

+2

重要的有趣的事實:strtok(和strtok_r)不是'new'指針指派給'p'。它接受你放入的字符串'line'並截取它,插入一個空字符來代替下一個找到的分隔符,然後返回一個指向現在以null結尾的字符串的第一個字符的指針。所以'刪除p;'實際上是試圖'刪除'''部分。嘉潮。好。也許不是,因爲你真正刪除的是NULL,因爲循環退出條件。不過,也許不應該這樣做。 – user4581301

+0

需要...這並不影響我的問題,雖然:( – tloveless

回答

3

只專注於內部循環和切斷所有的東西,我沒有看到必要的。

#include <iostream> 
#include <queue> 
#include <string> 
#include <cstring> 

using namespace std; 

int main() 
{ 
    std::queue<std::string> tks; 

    while(true) 
    { 
     char line[1024]; 
     char *savePtr; 
     char *p; 
     cin.getline(line, sizeof(line)); 
     p = strtok_r(line, " \n", &savePtr); // initial read. contents of savePtr ignored 
     while (p != NULL) // exit when no more data, which includes an emtpy line 
     { 
      tks.push(p); // got data, store it 
      p = strtok_r(NULL, " \n", &savePtr); // get next token 
     } 
     // consume tks 
    } 
} 

我更喜歡在在他的回答中使用由託比·斯佩特,因爲我覺得這是更透明,更易於閱讀循環while循環。你的旅費可能會改變。到編譯器完成時它們將是相同的。

沒有必要刪除任何內存。它全部靜態分配。除了tks之外,在下一輪之前沒有必要清除任何內容。 savePtr將由第一個strtok_r重置。

如果用戶在一行上輸入了1024個以上的字符,但不會崩潰,則會出現失敗情況。如果這仍然不起作用,請查看您的消費方式tks。它沒有發佈,所以我們不能排除那部分問題。

如果可能的話,全心全意推薦更改爲基於字符串的解決方案。這是一個非常簡單,易於編寫,但速度慢的方法之一:

#include <iostream> 
#include <queue> 
#include <string> 
#include <sstream> 

int main() 
{ 
    std::queue<std::string> tks; 

    while(true) 
    { 
     std::string line; 
     std::getline(std::cin, line); 
     std::stringstream linestream(line); 
     std::string word; 
     // parse only on ' ', not on the usual all whitespace of >> 
     while (std::getline(linestream, word, ' ')) 
     { 
      tks.push(word); 
     } 
     // consume tks 
    } 
} 
0

如果您可以選擇使用boost,請嘗試使用這一項來標記字符串。當然通過提供自己的字符串和分隔符。

#include <vector> 
#include <boost/algorithm/string.hpp> 

int main() 
{ 
    std::string str = "Any\nString\nYou want"; 

    std::vector<std::string> results; 
    boost::split(results, str, boost::is_any_of("\n")); 
} 
2

您的代碼將無法編譯我,所以我固定它:

#include <iostream> 
#include <queue> 
#include <string> 
#include <cstring> 

std::queue<std::string> tks; 

int main() { 
    char line[1024] = "one \ntwo \nthree\n"; 
    char *savePtr = 0; 

    for (char *p = strtok_r(line, " \n", &savePtr); p; 
          p = strtok_r(nullptr, " \n", &savePtr)) 
     tks.push(p); 

    // Did we read it correctly? 
    for (; tks.size() > 0; tks.pop()) 
     std::cout << ">" << tks.front() << "<" << std::endl; 
} 

這將產生預期的輸出:

>one< 
>two< 
>three< 

所以你的問題是不是與您發佈的代碼。