【问题标题】:Reading text file data into an array of structs (while ignoring comment lines)将文本文件数据读入结构数组(同时忽略注释行)
【发布时间】:2020-04-07 22:34:26
【问题描述】:

概述:

以下程序的目的是将输入文件中的数据逐行读取到结构数组中,同时忽略输入文件中以character '#' 开头的任何注释行。然后程序应该遍历结构数组并打印内容,以确认程序按预期工作。

这是输入文件的示例,其中可以看到 3 行非注释数据。非注释行的数量在编译之前是已知的,如下面的 int Nbodies = 3 行所示。

30 07 6991
# some comment
28 02 4991
09 09 2991

注意:请注意,在决定发布此问题之前,已经研究了以下 SO 问题等:

Reading a text file and ignoring commented lines in C

Ignoring comments when reading in a file

Read a text file ignoring comments

困境:

程序可以成功地将行读入结构数组并打印内容当没有注释行时。程序还可以成功检测行何时以'#'字符开头,因此将其视为注释行。问题在于,即使检测到注释行,程序仍会尝试错误地将这一行读入结构数组中。

这是预期的输出:

30 07 6991
28 02 4991
09 09 2991

这是实际的(和不正确的)输出,似乎忽略了最后一行未注释的数据:

30 07 6991
-842150451 -842150451 -842150451
28 02 4991

当前尝试:

fgets 已用于读取每一行,从而确定该行的开头是否以'#' 开头。此注释检查在IF 语句中执行,该语句增加FOR 循环条件中的Nbodies 变量(这样迭代不会“浪费”在注释行上,如果这有意义吗?)。在此之后,sscanf 用于尝试将当前 非注释行 的三个值读取到结构数组中。 fscanf 也是一种尝试过的方法,但没有奏效。通过在示例中看到的 IF 语句中使用continue;,如果检测到注释行,是否不应该“跳过”sscanf?它似乎没有按预期进行。

到目前为止的代码:

#include "stdio.h"

#define EXIT_SUCCESS 0
#define EXIT_FAILURE !EXIT_SUCCESS

int main() {

    typedef struct {
        int a1, b1, c1;
    }DATA;

    FILE *file = fopen("delete.nbody", "r");
    if (file == NULL)
    {
        printf(stderr, "ERROR: file not opened.\n");
        return EXIT_FAILURE;
    }

    int Nbodies = 3;
    int comment_count = 0;
    DATA* data = malloc(Nbodies * sizeof * data); // Dynamic allocation for array
    char line[128]; // Length won't be longer than 128
    int x;
    for (x = 0; x < Nbodies; x++)
    {
        fgets(line, sizeof(line), file);
        if (line[0] == '#')
        {
            comment_count++;
            Nbodies++;// Advance Nbodies so that iteration isn't 'wasted' on a comment line
            continue;
        }

        // QUESTION: doesn't "continue;" within above IF mean that the 
        // following sscanf shouldn't scan the comment line?
        sscanf(line, "%d %d %d", &data[x].a1, &data[x].b1, &data[x].c1);
    }

    // Nbodies - comment_count, because Nbodies advanced
    // every time a comment was detected in the above FOR loop
    for (x = 0; x < Nbodies - comment_count; x++)
    {
        printf("%d %d %d\n", data[x].a1, data[x].b1, data[x].c1);
    }

    return (EXIT_SUCCESS);
}

问题:

谁能明白为什么这个程序不能工作?我原以为continue 单词会在检测到时跳过 sscanf 读取注释行。任何帮助将不胜感激。

【问题讨论】:

  • fgets(line, sizeof(line), file); --> if (!fgets(line, sizeof line, file)) break; 您需要检查两个条件:数组大小和 EOF。你只检查其中一个。

标签: c arrays struct file-io fgets


【解决方案1】:

如果您收到评论,请不要增加Nbodies

这是错误的方法,因为data 被限制为Nbodies原始值,因为预循环malloc

而且,您将在data 数组中引入一个间隙

这里有一个更好的方法,让data 只包含有效数据。注释行变得完全“不可见”(即它们影响数据的计数):

int
main()
{

    typedef struct {
        int a1,
         b1,
         c1;
    } DATA;

    FILE *file = fopen("delete.nbody", "r");

    if (file == NULL) {
        printf(stderr, "ERROR: file not opened.\n");
        return EXIT_FAILURE;
    }

    int Nbodies = 3;
    int comment_count = 0;

    // Dynamic allocation for array
    DATA *data = malloc(Nbodies * sizeof *data);

    char line[128]; // Length won't be longer than 128
    int x;

#if 0
    for (x = 0; x < Nbodies; x++) {
        fgets(line, sizeof(line), file);

        if (line[0] == '#') {
            comment_count++;
            // Advance Nbodies so that iteration isn't 'wasted' on a comment
            Nbodies++;
            continue;
        }

        // QUESTION: doesn't "continue;" within above IF mean that the
        // following sscanf shouldn't scan the comment line?
        sscanf(line, "%d %d %d", &data[x].a1, &data[x].b1, &data[x].c1);
    }
#else
    int inc = 1;
    for (x = 0; x < Nbodies; x += inc) {
        fgets(line, sizeof(line), file);

        inc = (line[0] != '#');

        if (! inc) {
            comment_count++;
            continue;
        }

        // QUESTION: doesn't "continue;" within above IF mean that the
        // following sscanf shouldn't scan the comment line?
        sscanf(line, "%d %d %d", &data[x].a1, &data[x].b1, &data[x].c1);
    }
#endif

    // Nbodies - comment_count, because Nbodies advanced
    // every time a comment was detected in the above FOR loop
#if 0
    for (x = 0; x < Nbodies - comment_count; x++) {
#else
    for (x = 0; x < Nbodies; x++) {
#endif
        printf("%d %d %d\n", data[x].a1, data[x].b1, data[x].c1);
    }

    return (EXIT_SUCCESS);
}

【讨论】:

  • 这是一个很好的答案并且易于理解——这个功能完全符合要求。设置为答案。
【解决方案2】:
  • 只有在你实际分配时才增加数组索引
  • 检查两个条件:数组索引和输入成功

for (x = 0; x < Nbodies; ) {
        int rc;
        if ( !fgets(line, sizeof line, file)) break;
        if (line[0] == '#')
        {
            comment_count++;
            continue;
        }

        rc = sscanf(line, "%d %d %d", &data[x].a1, &data[x].b1, &data[x].c1);
        if (rc != 3) continue;
        x++; 
    }

【讨论】:

  • 感谢您提供尝试,您的观点很容易理解。我相信在if (rx != 3) continue 中看到的这种尝试可能存在语法错误...应该rx 这里是rc 吗?此外,FOR 循环条件中的增量器可能需要一些注意,但也许我错了......我得到了这个实现的意外输出,但我仍然感谢你的帮助。
  • 如果您的 incrementer 需要更多注意,您将需要一个额外的变量(包含 实际 读取案例数)。这也应该用作结构数组的索引。
  • 好的,现在全部排序。感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 2012-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-21
  • 2022-06-29
相关资源
最近更新 更多