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

时间:2015-11-02 19:58:36

标签: c++ parsing tokenize strtok

我从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

的长度给出不同的结果

我在这里缺少什么?

3 个答案:

答案 0 :(得分: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  
    }
}

我喜欢在他的回答中使用Toby Speight使用的for循环的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  
    }
}

答案 1 :(得分: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<

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

答案 2 :(得分: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" ) );
}