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