c使用fgets读取文件时,strtok会导致分段错误
试图逐行读取文件, 文件中的一行如下所示:c使用fgets读取文件时,strtok会导致分段错误,c,io,segmentation-fault,fgets,strtok,C,Io,Segmentation Fault,Fgets,Strtok,试图逐行读取文件, 文件中的一行如下所示: InputVector:0(1,3,4,2,40) 守则: FILE *file = fopen(filename, "r"); char buff[26]; char *token; while(fgets(buff, 26, (FILE*)file) != NULL) { buff[strlen(buff)] = '\0'; printf("%s\n", buff); token = strtok(buff, INV
InputVector:0(1,3,4,2,40)
守则:
FILE *file = fopen(filename, "r");
char buff[26];
char *token;
while(fgets(buff, 26, (FILE*)file) != NULL) {
buff[strlen(buff)] = '\0';
printf("%s\n", buff);
token = strtok(buff, INV_DELIM1);
printf("%s\n", token);
token = strtok(NULL, INV_DELIM2);
printf("%s\n", token);
while(token != NULL) {
token = strtok(NULL, INV_DELIM3);
printf("%s\n", token);
}
}
我的猜测是,在while循环中,strtok()没有在最后一个数字之后返回NULL
,并继续运行,导致分段错误。我尝试在fgets()
之后的buff
末尾添加“\0”
,但没有任何效果
delim1: ":",
delim2: "(",
delim3: ",)"
我得到的输出是
InputVector:0(1,3,4,2,40)
InputVector
0
1
3
4
2
40
segfault
您反复使用以下代码:
token = strtok(NULL, INV_DELIM2);
printf("%s\n", token);
如果strtok()
返回NULL,那么它将被传递到printf()
,由于format参数中的%s
,它将获得一个指向以0结尾的有效字符串的指针。NULL不是指向有效的以0结尾的字符串的指针,因此会发生不好的事情,在您的情况下表现为崩溃
解决方案:在尝试使用之前,请确保strok()返回的指针不为空
对未来的建议:学习如何使用调试器来逐步完成代码,并习惯使用valgrind来帮助跟踪内存问题。当您可以使用工具找出问题所在并准确了解问题所在时,您不必对发生的情况做出错误的猜测。不要吝啬于缓冲区大小。如果最长的行可以是25个
字符,则不要使用26个
字符作为缓冲区大小,而是:
#define MAXC 1024 /* constant for max characters in buf */
...
char buff[MAXC] = 1024;
(这取决于您,128
的工作原理与任何其他值一样,可以确保输入长度的任何变化都不会超出数组的边界。我宁愿缓冲区太长1000个字符,也不要太短1个字符。)
然后通过检查长度和buff
中的最后一个字符是否为'\n'
字符来验证每个fgets
调用,例如
while(fgets(buff, MAXC, file) != NULL) {
size_t len = strlen (buff);
if (len == MAXC - 1 && buff[len - 1] != '\n') {
fputs ("error: line too long.\n", stderr);
/* handle error - generally by reading and dicarding
* characters until '\n' or EOF encounterd and
* then either calling continue or break
*/
}
这将确保您在调用strtok
之前拥有一个有效的字符串
您不需要多个分隔符
然后,如注释中所述,不需要单独的分隔符。用#define delim:(,)\n“
定义的单个delim
或用const char*delim=“:(,)\n”
声明就足够了。然后,您可以简单地使用以下各项循环所有令牌:
for (token = strtok(buff, delim); token; token = strtok(NULL, delim))
printf ("%s\n", token);
简短示例
#include <stdio.h>
#include <string.h>
#define MAXC 1024
int main (int argc, char **argv) {
char buff[MAXC] = "";
char *token = NULL;
const char *delim = ":(,)\n";
FILE *file = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!file) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while(fgets(buff, MAXC, file) != NULL) {
size_t len = strlen (buff);
if (len == MAXC - 1 && buff[len - 1] != '\n') {
fputs ("error: line too long.\n", stderr);
/* handle error - generally by reading and dicarding
* characters until '\n' or EOF encounterd and
* then either calling continue or break
*/
}
for (token = strtok(buff, delim); token; token = strtok(NULL, delim))
printf ("%s\n", token);
}
if (file != stdin) fclose (file); /* close file if not stdin */
return 0;
}
仔细检查一下,如果您还有其他问题,请告诉我。这是否已经导致SegFault
buff[strlen(buff)]='\0'代码>?在尝试使用指针之前,您从不检查strtok()
是否返回空指针。在while
循环中,反转printf
和strtok
buff[strlen(buff)]='\0'代码>是无用的。如果buff
已NUL
终止,则它不执行任何操作。如果buff
尚未NUL
终止,则这是未定义的行为。我喜欢buffer[strcspn(buffer,“\n”)]='\0'代码>因为它避免了代码中的条件,并且在字符串为空时起作用,等等@JonathanLeffler是的,你有一个很好的观点。我想是习惯吧。我通常使用strlen
,因此我的常规检查是如果(len&buff[len-1]='\n')buff[--len]='\0';else如果(len==MAXC-1){/*字符串太长*/}
。(截断换行符后,保留len
中更新的字符串长度)这里没有继续使用buff
作为需要删除换行符的完整字符串,因此我只是稍微缩短了测试,以检查行是否适合buff
。
$ echo "InputVector:0(1,3,4,2,40)" | ./bin/strtok_delims
InputVector
0
1
3
4
2
40