C 中 strtok 的问题
Issues with strtok in C
我的代码中有以下提取名称的函数:
void student_init(struct student *s, char *info) {
char *name;
char *name2;
char a[] = { *info };
name = strtok(a, "/");
strcpy(s->first_name, name);
name2 = strtok(NULL, "/");
strcpy(s->last_name, name2);
}
当我运行这个时,我看到:
Please enter a student information or enter "Q" to quit.
Daisy/Lee
A student information is read.: Daisy/Lee
Please enter a row number where the student wants to sit.
1
Please enter a column number where the student wants to sit.
2
The seat at row 1 and column 2 is assigned to the student: D . �
?.?. ?.?. ?.?.
?.?. ?.?. D.�.
?.?. ?.?. ?.?.
我正在尝试使用 c 程序中的 strtok 函数以“/”拆分字符串以分隔拳头和姓氏并将它们存储在 a 的 first_name 和 last_name 变量中学生结构。我可以获取存储在相应变量中的名字,但正如您从上面 link 中的图像中看到的那样,我得到的是 ?输出中姓氏的第一个首字母所在的符号。
char a[] = { *info };
这是你的问题。这将创建一个单字节字符数组,其中包含 info
的第一个字符,仅此而已。
因为 strtok
需要一个 字符串, 它可能会 运行 离开那个单字节数组的末尾并使用那里发生的任何事情在记忆中。这就是为什么您看到第一个字符没问题而没有其他字符的原因(尽管从技术上讲,作为未定义的行为,字面上 任何事情 都允许发生)。
与其构建单字节数组,不如直接使用传入的字符串:
name = strtok(info, "/");
您制作本地副本的唯一原因是您正在标记化的字符串不允许更改(例如,如果它是字符串文字,或者您想要保留它供以后使用)。由于您的示例 运行 表明您正在将 读入 此字符串,因此它不能是字符串文字。
而且,如果您想保留它以备后用,这可能是调用者而不是函数产生的最佳成本(当关于它是否是 是否需要只有调用者知道)。
为了进行标记化复制,很简单:
char originalString[] = "pax/diablo";
scratchString = strdup(originalString);
if (scratchString != NULL) {
student_init (struct student *s, scratchString);
free (scratchString);
} else {
handleOutOfMemoryIssue();
}
useSafely (originalString);
如果您的实施 没有 strdup
(它是 POSIX 而不是 ISO),请参阅 here。
在我看来,"cleaner" 的实现方式如下:
void student_init (struct student *s, char *info) {
// Default both to empty strings.
*(s->first_name) = '[=13=]';
*(s->last_name) = '[=13=]';
// Try for first name, exit if none, otherwise save.
char *field = strtok (info, "/");
if (field == NULL) return;
strcpy (s->first_name, field);
// Try for second name, exit if none, otherwise save.
if ((field = strtok (NULL, "/")) == NULL) return;
strcpy (s->last_name, field);
}
我的代码中有以下提取名称的函数:
void student_init(struct student *s, char *info) {
char *name;
char *name2;
char a[] = { *info };
name = strtok(a, "/");
strcpy(s->first_name, name);
name2 = strtok(NULL, "/");
strcpy(s->last_name, name2);
}
当我运行这个时,我看到:
Please enter a student information or enter "Q" to quit.
Daisy/Lee
A student information is read.: Daisy/Lee
Please enter a row number where the student wants to sit.
1
Please enter a column number where the student wants to sit.
2
The seat at row 1 and column 2 is assigned to the student: D . �
?.?. ?.?. ?.?.
?.?. ?.?. D.�.
?.?. ?.?. ?.?.
我正在尝试使用 c 程序中的 strtok 函数以“/”拆分字符串以分隔拳头和姓氏并将它们存储在 a 的 first_name 和 last_name 变量中学生结构。我可以获取存储在相应变量中的名字,但正如您从上面 link 中的图像中看到的那样,我得到的是 ?输出中姓氏的第一个首字母所在的符号。
char a[] = { *info };
这是你的问题。这将创建一个单字节字符数组,其中包含 info
的第一个字符,仅此而已。
因为 strtok
需要一个 字符串, 它可能会 运行 离开那个单字节数组的末尾并使用那里发生的任何事情在记忆中。这就是为什么您看到第一个字符没问题而没有其他字符的原因(尽管从技术上讲,作为未定义的行为,字面上 任何事情 都允许发生)。
与其构建单字节数组,不如直接使用传入的字符串:
name = strtok(info, "/");
您制作本地副本的唯一原因是您正在标记化的字符串不允许更改(例如,如果它是字符串文字,或者您想要保留它供以后使用)。由于您的示例 运行 表明您正在将 读入 此字符串,因此它不能是字符串文字。
而且,如果您想保留它以备后用,这可能是调用者而不是函数产生的最佳成本(当关于它是否是 是否需要只有调用者知道)。
为了进行标记化复制,很简单:
char originalString[] = "pax/diablo";
scratchString = strdup(originalString);
if (scratchString != NULL) {
student_init (struct student *s, scratchString);
free (scratchString);
} else {
handleOutOfMemoryIssue();
}
useSafely (originalString);
如果您的实施 没有 strdup
(它是 POSIX 而不是 ISO),请参阅 here。
在我看来,"cleaner" 的实现方式如下:
void student_init (struct student *s, char *info) {
// Default both to empty strings.
*(s->first_name) = '[=13=]';
*(s->last_name) = '[=13=]';
// Try for first name, exit if none, otherwise save.
char *field = strtok (info, "/");
if (field == NULL) return;
strcpy (s->first_name, field);
// Try for second name, exit if none, otherwise save.
if ((field = strtok (NULL, "/")) == NULL) return;
strcpy (s->last_name, field);
}