c使用fgets读取文件,strtok导致分段错误

时间:2018-09-12 07:34:31

标签: 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_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,并且持续运行并导致分段错误。我尝试在"\0"之后的buff末尾添加fgets(),但是它什么也没做。

delim1: ":",
delim2: "(",
delim3: ",)"

我得到的输出是

InputVector:0(1,3,4,2,40)
InputVector
0
1
3
4
2
40
segfault

2 个答案:

答案 0 :(得分:4)

您反复输入以下代码:

token = strtok(NULL, INV_DELIM2);
printf("%s\n", token);

如果strtok()返回NULL,则将其传递到printf(),由于format参数中的%s,它期望获得指向以0终止的有效字符串的指针。 NULL不是指向有效的以0结尾的字符串的指针,因此发生坏事,在您的情况下表现为崩溃。

解决方案:在尝试使用strtok()返回的指针之前,请确保其不为NULL。

对未来的建议:了解如何使用调试器逐步遍历代码,并习惯于使用 valgrind 帮助跟踪内存问题。当您可以使用工具找出问题所在并确切了解问题出在哪里时,您不必对正在发生的事情做出错误的猜测。

答案 1 :(得分:2)

不要跳过缓冲区大小。如果最长行可以是25个字符,则不要使用26个字符作为缓冲区大小,而是:

#define MAXC 1024  /* constant for max characters in buf */
...
    char buff[MAXC] = 1024;

(由您决定,128的工作原理与其他任何值一样,可以保证输入长度的任何变化都不会超出数组范围。我宁愿缓冲区为1000-字符长于1个字符,太短。)

然后通过检查长度并验证fgets中的最后一个字符为buff字符来验证每个'\n'调用,例如

    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之前具有有效的字符串。

您不需要多个定界符

然后,如注释中所述,不需要单独的定界符。用delim定义或用#define DELIM ":(,)\n"声明的单个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;
}

(如果需要其他结果,可以调整delim

使用/输出示例

$ echo "InputVector:0(1,3,4,2,40)" | ./bin/strtok_delims
InputVector
0
1
3
4
2
40

仔细检查一下,如果还有其他问题,请告诉我。