C strdup和strtok导致的SEGDULT
我的大学教授给我布置了一个家庭作业,我似乎发现了strtok的一些奇怪行为 基本上,我们必须为我的类解析一个CSV文件,其中CSV中的令牌数是已知的,最后一个元素可能有额外的C strdup和strtok导致的SEGDULT,c,strtok,C,Strtok,我的大学教授给我布置了一个家庭作业,我似乎发现了strtok的一些奇怪行为 基本上,我们必须为我的类解析一个CSV文件,其中CSV中的令牌数是已知的,最后一个元素可能有额外的,“字符 行的一个示例: Hello,World,This,Is,A lot, of Text 其中令牌应输出为 1. Hello 2. World 3. This 4. Is 5. A lot, of Text 对于此任务,我们必须使用strtok。因此,我在其他一些SOF文章中发现,使用带有空字符串的strtok(或
,“
字符
行的一个示例:
Hello,World,This,Is,A lot, of Text
其中令牌应输出为
1. Hello
2. World
3. This
4. Is
5. A lot, of Text
对于此任务,我们必须使用strtok
。因此,我在其他一些SOF文章中发现,使用带有空字符串的strtok
(或将“\n”
作为第二个参数传递)会导致读取到行尾。这非常适合我的应用程序,因为额外的逗号总是出现在最后一个元素中
我创建了以下代码:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define NUM_TOKENS 5
const char *line = "Hello,World,This,Is,Text";
char **split_line(const char *line, int num_tokens)
{
char *copy = strdup(line);
// Make an array the correct size to hold num_tokens
char **tokens = (char**) malloc(sizeof(char*) * num_tokens);
int i = 0;
for (char *token = strtok(copy, ",\n"); i < NUM_TOKENS; token = strtok(NULL, i < NUM_TOKENS - 1 ? ",\n" : "\n"))
{
tokens[i++] = strdup(token);
}
free(copy);
return tokens;
}
int main()
{
char **tokens = split_line(line, NUM_TOKENS);
for (int i = 0; i < NUM_TOKENS; i++)
{
printf("%s\n", tokens[i]);
free(tokens[i]);
}
}
当你到达最后一个环路时,你将得到
for (char *token = strtok(copy, ",\n"); i < NUM_TOKENS - 1; token = strtok(NULL, ",\n"))
\n
在那里,甚至需要调用最后一个strtok(这不是已经指向字符串的其余部分了吗?如果你只是想切掉一个尾随的换行符,还有更简单的方法),但我已经好几年没有使用strtok了
(顺便说一句,您也没有释放存储字符串指针的malloced数组。这是因为程序在该点结束并不重要。)请记住,当它在分隔符字符串中找到任何字符时(strtok()的第二个参数)-它不会尝试匹配整个分隔符字符串本身
因此,一开始就不需要三元运算符-字符串将根据输入字符串中出现的、
或\n
进行标记,因此以下操作有效:
for (token = strtok(copy, ",\n"); i < NUM_TOKENS; token = strtok(NULL, ",\n"))
{
tokens[i++] = strdup(token);
}
在将
NULL
传递给另一个函数之前,请检查strtok中的返回值。您的循环调用strtok的次数比您想象的要多
通常使用这个返回值来控制循环,这样就不会受数据的支配。至于定界符,最好保持简单,不要尝试任何花哨的东西
char **split_line(const char *line, int num_tokens)
{
char *copy = strdup(line);
char **tokens = (char**) malloc(sizeof(char*) * num_tokens);
int i = 0;
char *token;
char delim1[] = ",\r\n";
char delim2[] = "\r\n";
char *delim = delim1; // start with a comma in the delimiter set
token = strtok(copy, delim);
while(token != NULL) { // strtok result comtrols the loop
tokens[i++] = strdup(token);
if(i == NUM_TOKENS) {
delim = delim2; // change the delimiters
}
token = strtok(NULL, delim);
}
free(copy);
return tokens;
}
注意:如果查看问题的
5,还应检查malloc
和strdup
的返回值,并正确释放内存。大量包含逗号的文本
,您将看到OP想要更改定界符的原因。如果您查看我的问题,您将看到为什么我需要\n作为最后一个案例。看一下需要打印的内容。看看“5.”输出的情况。我理解为什么没有逗号,但我在输入字符串中没有看到任何换行符。我会清理内存管理。如果这是另一种选择,我会坚持我的三元结构。我认为这个问题需要一个for循环模式。谢谢你的帮助!谢谢你的投票。for
forstrtok
的循环非常难看,也不惯用,而且正如您所发现的那样,非常麻烦。如果您担心此处的行数,可以删除所有delim变量,将第一个“,\n”或“,\r\n”设为此处,然后将三元数放在最后一行strtok中。for循环的优点是它保证不会使令牌数组溢出:缺点是不能很好地处理具有较少段的输入。这两种方法都可以解决,但是您的调用代码需要接受元素较少的返回值或返回分配的空字符串等。我想让代码变得明显。我本可以使用单个分隔符字符串,\r\n“
,并在不再需要,”
时简单地增加字符串指针。请简单明了地写代码。你可能有一个“幻想”,你想让一些方法工作,但是首要的考虑应该是可读性和可维护性,以及调试复杂表达式结果所花费的最短时间。这根本不是我的观点。OP说他不打算使用你的代码,因为他认为它太复杂了,所以他会使用他的三元代码。我告诉他如何用更少的行数使用你的代码。啊,我现在看到了混乱。将strtok与\n一起使用是逐行解析加载到内存中的整个文本文件的方法。如果你只处理一行,它本身不包含换行符,那么它就没有神奇的意义。
for (char *token = strtok(copy, ",\n"); i < NUM_TOKENS - 1; token = strtok(NULL, ",\n"))
int i = 0;
tokens[i++] = strdup(strtok(copy, ",\n"));
for (token = strtok(copy, ",\n"); i < NUM_TOKENS; token = strtok(NULL, ",\n"))
{
tokens[i++] = strdup(token);
}
for (token = strtok(copy, ",\n"); i < NUM_TOKENS - 1; token = strtok(NULL, ",\n"))
{
tokens[i++] = strdup(token);
}
tokens[i] = strdup(token);
char **split_line(const char *line, int num_tokens)
{
char *copy = strdup(line);
char **tokens = (char**) malloc(sizeof(char*) * num_tokens);
int i = 0;
char *token;
char delim1[] = ",\r\n";
char delim2[] = "\r\n";
char *delim = delim1; // start with a comma in the delimiter set
token = strtok(copy, delim);
while(token != NULL) { // strtok result comtrols the loop
tokens[i++] = strdup(token);
if(i == NUM_TOKENS) {
delim = delim2; // change the delimiters
}
token = strtok(NULL, delim);
}
free(copy);
return tokens;
}