在 C++ 的 while 循环中使用 strtok/strtok_r

Using strtok/strtok_r in a while loop in C++

我从 strtok 和 strtrok_r 函数中得到意外行为:

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

的长度给出不同的结果

我在这里错过了什么?

如果您可以选择使用 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" ) );
}

你的代码无法为我编译,所以我修复了它:

#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<

所以您的问题不在于您发布的代码。

只关注内部循环并删除所有我认为没有必要的东西。

#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 循环而不是 Toby Speight 在他的回答中使用的 for 循环,因为我认为它更透明且更易于阅读。你的旅费可能会改变。到编译器完成时,它们将完全相同。

不需要删除任何记忆。都是静态分配的。除了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  
    }
}