C++ Strtok在C++;给了我意想不到的行为

C++ Strtok在C++;给了我意想不到的行为,c++,C++,我有这个函数: void simple_shell::parse_command(char* cmd, char** cmdTokens) { // TODO: tokenize the command string into arguments char *token = strtok(cmd, " "); int count = 0; while (token != NULL) { count = count + 1; cout << t

我有这个函数:

void simple_shell::parse_command(char* cmd, char** cmdTokens) {
  // TODO: tokenize the command string into arguments
  char *token = strtok(cmd, " ");
  int count = 0;

  while (token != NULL) {
      count = count + 1;
      cout << token << endl;
      token = strtok(NULL, " ");
  }

  char *secondToken = strtok(cmd, " ");
  *cmdTokens = new char[count];
  count = 0;

  while (secondToken != NULL) {
      cmdTokens[count] = secondToken;
      cout << secondToken << endl;
      secondToken = strtok(NULL, " ");
      count = count + 1;
  }
}
void simple_shell::parse_命令(char*cmd,char**cmdTokens){
//TODO:将命令字符串标记为参数
char*token=strtok(cmd,“”);
整数计数=0;
while(令牌!=NULL){
计数=计数+1;
cout
strtok()
将以null结尾的字符串作为输入进行操作,但它会在字符串运行时修改字符串。当它在字符串中找到分隔符时,会在分隔符所在的位置插入nul字符,并返回指向插入的nul前面的标记的指针。在使用null输入指针的下一次调用中,搜索将在上一个nul之后开始y注入nul

因此,当您运行第一个循环时,所有内容都正确输出,但是
strtok()
更改了
cmd
的内容,将所有空格字符替换为nul字符。因此,当您运行第二个循环时,它在
cmd
中找不到任何空格字符,并在到达第一个标记后停止查找nul字符,即使它实际上不是字符串中的最后一个nul

所以,你有几个选择

您可以在每个循环上复制
cmd
。您还必须复制存储在输出数组中的每个令牌,因为它们将指向临时内存:

int simple_shell::parse_command(char* cmd, char*** cmdTokens)
{
    *cmdTokens = NULL;

    char *dup = strdup(cmd);
    if (!dup)
        return -1;

    int count = 0;

    char *token = strtok(dup, " ");
    while (token)
    {
        ++count;
        token = strtok(NULL, " ");
    }

    free(dup);

    try {
        *cmdTokens = new char*[count];
    }
    catch (const std::bad_alloc&) {
        return -1;
    }

    if (count > 0)
    {
        dup = strdup(cmd);
        if (!dup)
        {
            delete[] *cmdTokens;
            return -1;
        }

        token = strtok(dup, " ");
        count = 0;

        while (token)
        {
            cmdTokens[count] = strdup(token);
            if (!cmdTokens[count])
            {
                for(int i = 0; i < count; ++i)
                    free(cmdTokens[i]);
                delete[] *cmdTokens;
                return -1;
            }

            ++count;
            token = strtok(NULL, " ");
        }
    }

    return count;
}

char**tokens;
int numTokens=shell.parse_命令(“helloworld”、&tokens);
如果(numTokens!=-1)
{
对于(int i=0;iSTD::C++在C++中不使用<代码> Sttokor()/Case>来分割字符串。对于重新进入来说,它容易出错,而且不可靠。这里有一些更好的方法:在使用C++时,也不要停止使用<代码> char *<代码>和<代码>新< /代码>。
修改它的缓冲区参数。这就是它的工作原理。阅读它的描述。因此,在第一次遍历字符串时,
char
缓冲区被彻底破坏,并被
strtok()
。显示的代码天真地期望
cmd
在第二次通过时成为其原始的原始自我。不幸的是,这不会发生。祝你下次好运。我基本上使用cmd并将其以空格作为分隔符进行拆分。--有趣。将该代码与您尝试的
strtok
进行比较。有什么建议吗n获取我需要的输出?在每个循环之前使用
strdup(cmd)
,然后标记重复的字符串,或者根本不要使用
strtok()
。还有其他方法可以标记字符串而不改变它。@HumanCyborgRelations放弃
strtok()
并使用更稳定的东西。@Remy“使用
strdup(cmd)
每次循环“我闻到内存泄漏”之前:P@user0042当然,但是当使用C++风格的内存管理时,会发生这种情况。
char** tokens;
int numTokens = shell.parse_command("hello world", &tokens);
if (numTokens != -1)
{
    for (int i = 0; i < numTokens; ++i)
    {
        std::cout << tokens[i] << std::endl;
        free(tokens[i]);
    }
    delete[] tokens;
}
const char* nextToken(const char *str)
{
    if (!str)
        return NULL;

    while (*str == ' ')
        ++str;

    if (*str == '\0')
        return NULL;

    return str;
}

const char* endOfToken(const char *str)
{
    if (!str)
        return NULL;

    while ((*str != ' ') && (*str != '\0'))
        ++str;

    return ptr;
}

int simple_shell::parse_command(char* cmd, char*** cmdTokens)
{
    *cmdTokens = NULL;

    char *ptr = cmd;
    int count = 0;

    const char *token = nextToken(ptr);
    while (token)
    {
        ++count;
        token = nextToken(endOfToken(token));
    }

    try {
        *cmdTokens = new char*[count];
    }
    catch (const std::bad_alloc&) {
        return -1;
    }

    if (count > 0)
    {
        ptr = cmd;
        count = 0;

        token = nextToken(ptr);
        while (token)
        {
            const char *end = endOfToken(token);
            int len = (end-token);

            try {
                cmdTokens[count] = new char[len+1];
            }
            catch (const std::bad_alloc&) {
                for(int i = 0; i < count; ++i)
                    delete[] cmdTokens[i];
                return -1;
            }

            memcpy(cmdTokens[count], token, len);
            cmdTokens[count][len] = '\0';
            ++count;

            token = nextToken(end);
        }
    }

    return count;
}
char** tokens;
int numTokens = shell.parse_command("hello world", &tokens);
if (numTokens != -1)
{
    for (int i = 0; i < numTokens; ++i)
    {
        std::cout << tokens[i] << std::endl;
        delete[] tokens[i];
    }
    delete[] tokens;
}
struct token_info
{
    char* token;
    int length;
}

const char* nextToken(const char *str)
{
    if (!str)
        return NULL;

    while (*str == ' ')
        ++str;

    if (*str == '\0')
        return NULL;

    return str;
}

const char* endOfToken(const char *str)
{
    if (!str)
        return NULL;

    while ((*str != ' ') && (*str != '\0'))
        ++str;

    return ptr;
}

int simple_shell::parse_command(char* cmd, token_info** cmdTokens)
{
    *cmdTokens = NULL;

    char *ptr = cmd;
    int count = 0;

    const char *token = nextToken(ptr);
    while (token)
    {
        ++count;
        token = nextToken(endOfToken(token));
    }

    try {
        *cmdTokens = new token_info[count];
    }
    catch (const std:bad_alloc&) {
        return -1;
    }

    if (count > 0)
    {
        ptr = cmd;
        count = 0;

        token = nextToken(ptr);
        while (token)
        {
            const char *end = endOfToken(token);

            cmdTokens[count].token = const_cast<char*>(token);
            cmdTokens[count].length = (end-token);
            ++count;

            token = nextToken(end);
        }
    }

    return count;
}
token_info* tokens;
int numTokens = shell.parse_command("hello world", &tokens);
if (numTokens != -1)
{
    for (int i = 0; i < numTokens; ++i)
        std::cout << std::string(tokens[i].token, tokens[i].length) << std::endl;
    delete[] tokens;
}
std::vector<std::string> simple_shell::parse_command(const std::string &cmd)
{
    std::istringstream iss(cmd);
    return std::vector<std::string>(
        std::istream_iterator<std::string>(iss),
        std::istream_iterator<std::string>()
    );
}
std::vector<std::string> tokens = shell.parse_command("hello world");
for (auto &token: tokens)
    std::cout << token << std::endl;