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