【问题标题】:C strtok() split string into tokens but keep old data unalteredC strtok() 将字符串拆分为标记,但保持旧数据不变
【发布时间】:2013-06-14 09:08:39
【问题描述】:

我有以下代码:

#include <stdio.h>
#include <string.h>

int main (void) {
    char str[] = "John|Doe|Melbourne|6270|AU";

    char fname[32], lname[32], city[32], zip[32], country[32];
    char *oldstr = str;

    strcpy(fname, strtok(str, "|"));
    strcpy(lname, strtok(NULL, "|"));
    strcpy(city, strtok(NULL, "|"));
    strcpy(zip, strtok(NULL, "|"));
    strcpy(country, strtok(NULL, "|"));

    printf("Firstname: %s\n", fname);
    printf("Lastname: %s\n", lname);
    printf("City: %s\n", city);
    printf("Zip: %s\n", zip);
    printf("Country: %s\n", country);
    printf("STR: %s\n", str);
    printf("OLDSTR: %s\n", oldstr);

    return 0;
}

执行输出:

$ ./str
Firstname: John
Lastname: Doe
City: Melbourne
Zip: 6270
Country: AU
STR: John
OLDSTR: John

为什么我不能保留旧数据,也不能保留在stroldstr 中,我做错了什么,如何不更改或保留数据?

【问题讨论】:

  • xtmtrx here in my answer 我写了一段代码来说明strtok() 是如何工作的(它修改了同一地址空间中的字符串),我想你应该看看:
  • 我希望人们在提出这样的问题之前阅读 strtok() 函数的源代码或阅读函数文档。

标签: c strtok


【解决方案1】:

当您执行strtok(NULL, "|") strtok() 时,找到令牌并将null 放在适当的位置(将令牌替换为\0)并修改字符串。

str,变成:

char str[] = John0Doe0Melbourne062700AU;
                 
  Str array in memory 
+------------------------------------------------------------------------------------------------+
|'J'|'o'|'h'|'n'|0|'D'|'o'|'e'|0|'M'|'e'|'l'|'b'|'o'|'u'|'r'|'n'|'e'|0|'6'|'2'|'7'|'0'|0|'A'|'U'|0|
+------------------------------------------------------------------------------------------------+
                 ^  replace | with \0  (ASCII value is 0)

考虑到图表很重要,因为 char '0'0 不同(在字符串 6270 中,图中的 char 由 ' 括起来,其中 \0 0 是数字)

当您使用 %s 打印 str 时,它会打印字符到第一个 \0John

要保持原始 str 不变,您应该先将 str 复制到某个 tempstr 变量中,然后在 strtok() 中使用该 tempstr 字符串:

char str[] = "John|Doe|Melbourne|6270|AU";
char* tempstr = calloc(strlen(str)+1, sizeof(char));
strcpy(tempstr, str);

现在在您的代码中使用这个tempstr 字符串代替str。

【讨论】:

  • 一个精心编译的答案 +1 :)
【解决方案2】:

因为oldstr 只是一个指针,所以赋值不会复制你的字符串。

在将 str 传递给 strtok 之前复制它:

          char *oldstr=malloc(sizeof(str));
          strcpy(oldstr,str);

您的更正版本:

#include <stdio.h>
#include <string.h>
#include<malloc.h>
int main (void) {

   char str[] = "John|Doe|Melbourne|6270|AU";
   char fname[32], lname[32], city[32], zip[32], country[32];
   char *oldstr = malloc(sizeof(str));
   strcpy(oldstr,str);

    ...................
    free(oldstr);
return 0;
}

编辑:

正如@CodeClown 提到的,在您的情况下,最好使用strncpy。而不是事先固定fname 等的大小,您可以在它们的位置放置指针并根据需要分配内存,不多也不少。这样就可以避免越界写入缓冲区......

另一个想法: 将strtok 的结果分配给指针*fname*lname 等,而不是数组。似乎strtok 被设计为在看到接受的答案后以这种方式使用。

注意:通过这种方式,如果您进一步更改str,这也会反映在fname,lname 中。因为,它们只是指向str 数据而不是新的内存块。因此,请使用oldstr 进行其他操作。

#include <stdio.h>
#include <string.h>
#include<malloc.h>
int main (void) {

    char str[] = "John|Doe|Melbourne|6270|AU";
    char *fname, *lname, *city, *zip, *country;
    char *oldstr = malloc(sizeof(str));
    strcpy(oldstr,str);
    fname=strtok(str,"|");
    lname=strtok(NULL,"|");
    city=strtok(NULL, "|");
    zip=strtok(NULL, "|");
    country=strtok(NULL, "|");

    printf("Firstname: %s\n", fname);
    printf("Lastname: %s\n", lname);
    printf("City: %s\n", city);
    printf("Zip: %s\n", zip);
    printf("Country: %s\n", country);
    printf("STR: %s\n", str);
    printf("OLDSTR: %s\n", oldstr);
    free(oldstr);
return 0;
}

【讨论】:

  • 好答案!也许使用strncpy() 并且不要使用静态缓冲区,因为某些人或城市的名称长度超过 32 个字符。他的下一个问题是关于堆栈损坏:-)
  • 在一个真正的程序中——而不是仅仅提出一个问题——不要忘记freeoldstr使用的内存。
  • @Code Clown,是的,堆栈损坏:P 通过 Grijesh Chauhan 的回答解决了
  • 是的,我愿意 free(str)free(oldstr)
  • @pinkpanther 这里不是,但在工作代码中我​​的str 是动态分配的(来自网络的卷曲数据)。
【解决方案3】:

strtok 需要一个可写的输入字符串,它会修改输入字符串。如果你想保留输入字符串,你必须先复制它。

例如:

char str[] = "John|Doe|Melbourne|6270|AU";
char oldstr[32];

strcpy(oldstr, str);  // Use strncpy if you don't know
                      // the size of str

【讨论】:

  • 最好先获取字符串的长度,然后创建一个合适的数据,因为人们有不同的名字和居住在不同的城市。
【解决方案4】:

您只需将指针复制到字符串,而不是字符串本身。使用strncpy() 创建副本。

char *oldstr = str; // just copy of the address not the string itself!

【讨论】:

  • 这不会阻止 strtok() 更改字符串的内容。
  • @EugeneBujak 是的,但仅在副本中。原件保持不变。
  • 那么为什么你的答案中没有包含 strcpy() 的使用?
猜你喜欢
  • 2011-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-24
  • 1970-01-01
  • 1970-01-01
  • 2021-03-30
  • 2013-09-15
相关资源
最近更新 更多