fgets()在fscanf()之后无法正常工作

时间:2013-10-14 15:39:16

标签: c newline fgets scanf

我使用fscanf读取日期,然后fgets读取笔记。 但是,在第一次迭代之后,fscanf返回值-1。

我使用GDB逐步调试程序。它在第一次使用fgets之前工作正常。当我尝试在第一次迭代时打印出由fgets读取的行时,它给了我:

(gdb) print line
$6 = "\rtest\r18/04/2010\rtest2\r03/05/2010\rtest3\r05/08/2009\rtest4\r\n\000\000\000\000q\352\261\a\370\366\377\267.N=\366\000\000\000\000\003\000\000\000\370xC\000\000\000\000\000\000\000\000\000\001\000\000\000\227\b\000\000\070\367\377\267H\364\377\267\362\202\004\bdoD\000\354\201\004\b\001\000\000\000\304oC\000p\363\377\277\260zC\000D\363\377\277\n!B\000\064\363\377\277\354\201\004\b(\363\377\277TzC\000\000\000\000\000\070\367\377\267\001\000\000\000\000\000\000\000\001\000\000\000\370xC\000\001\000\000\000\000\000\312\000\000\000\000\000\377\260\360\000\001\000\000\000\277\000\000\000\364\317\000\000\344\261\\\000\000\000\000\000p\363\377\277|\233\004\b\350\362\377\277 \204\004\b\005\000\000\000|\233\004\b\030\363\377\277"

看起来fgets会读取剩余的条目,然后将它们全部存储在一个字符串中。

我不确定为什么这样做。

这是主要代码:

int main(int argc, char* argv[]) {
    FILE* file;
    int numEntries, i = 0;
    int index = atoi(argv[1]);
    char line[SIZE];
    JournalEntry *entry;

    /*argument provided is the entry user wants to be displayed*/
    if (argc > 2) {
        perror("Error: Too many arguments provided");
    }
    file = fopen("journalentries.txt", "r");
    if (file == NULL) {
        perror("Error in opening file");
    }

    if (fscanf(file, "%d", &numEntries) != 1) {
        perror("Unable to read number of entries");
    }

    entry = (JournalEntry*)malloc(numEntries  * sizeof(JournalEntry));
    if (entry == NULL) {
        perror("Malloc failed");
    }

    for (i = 0; i < numEntries; i++) {
        if (fscanf(file, "%d/%d/%d", &entry[i].day, &entry[i].month, &entry[i].year) != 3) {
            perror("Unable to read date of entry");
        }

        if (fgets(line, sizeof(line), file) == NULL) {
            perror("Unable to read text of entry");
        }
    }

    printf("%d-%02d-%02d %s: ", entry[index].year, entry[index].month, entry[index].day, entry[index].text);

    if(ferror(file)) {
        perror("Error with file");
    }

    fclose(file);
    free(entry);

    return 0;
}

我必须阅读的文件: 第一行包含要读取的条目数

4
12/04/2010
test
18/04/2010
test2
03/05/2010
test3
05/08/2009
test4

结构JournalEntry位于头文件中:

typedef struct {
    int day;
    int month;
    int year;
    char text[250];
} JournalEntry;

3 个答案:

答案 0 :(得分:1)

  

看起来fgets会读取剩余的条目,然后将它们全部存储在一个字符串中。

是的,'\r'不是行终止符。因此,当fscanf停止解析第一个无效字符并将它们留在缓冲区中时,fgets将读取它们直到行尾。由于文件中没有有效的行终止符,因此直到文件结尾。

您应该修复该文件以获得有效的(Unix?)行结尾,例如使用合适的文本编辑器即可。但这是另一个问题,之前已被问过(如here),并取决于您的问题中未包含的详细信息。

此外,您需要对fscanf返回值进行双重检查。仅当返回值为-1时才使用perror,否则错误消息根本不会与错误相关。如果返回值为>=0但与您想要的不同,则打印自定义错误消息“无效输入语法”或其他任何内容(并且可能使用fgets从缓冲区中读取其余行。)< / p>

另外,为了可靠地混合scanffgets,我需要在fscanf格式字符串中添加空格,因此它会读取行尾的任何空格(也是在下一行的开头和任何空行,所以要小心,如果这很重要),像这样:

int items_read = scanf("%d ", &intvalue);

如另一个答案中所述,最好只阅读fgets行,然后逐行sscanf解析。

答案 1 :(得分:1)

不要混用fscanf()fgets(),因为前者可能会将内容留在流的缓冲区中。

对于面向行的格式,请使用fgets()只读完整行,然后使用例如sscanf()解析你所读过的内容。

答案 2 :(得分:0)

运行GDB时看到的字符串确实以第一个空字符结束:

"\rtest\r18/04/2010\rtest2\r03/05/2010\rtest3\r05/08/2009\rtest4\r\n\000"

忽略其他数据(使用普通的str函数时);