【问题标题】:Problems parsing a file in C在 C 中解析文件的问题
【发布时间】:2013-05-09 08:20:41
【问题描述】:

我正在尝试解析一组格式相同的文件。这是一个示例:

NAME:  br17
TYPE: ATSP
COMMENT: 17 city problem (Repetto)
DIMENSION:  17
EDGE_WEIGHT_TYPE: EXPLICIT
EDGE_WEIGHT_FORMAT: FULL_MATRIX 
EDGE_WEIGHT_SECTION
 9999    3    5   48   48    8    8    5    5    3    3    0    3    5    8    8
    5
    3 9999    3   48   48    8    8    5    5    0    0    3    0    3    8    8
    5
    5    3 9999   72   72   48   48   24   24    3    3    5    3    0   48   48
   24
   48   48   74 9999    0    6    6   12   12   48   48   48   48   74    6    6
   12
   48   48   74    0 9999    6    6   12   12   48   48   48   48   74    6    6
   12
    8    8   50    6    6 9999    0    8    8    8    8    8    8   50    0    0
    8
    8    8   50    6    6    0 9999    8    8    8    8    8    8   50    0    0
    8
    5    5   26   12   12    8    8 9999    0    5    5    5    5   26    8    8
    0
    5    5   26   12   12    8    8    0 9999    5    5    5    5   26    8    8
    0
    3    0    3   48   48    8    8    5    5 9999    0    3    0    3    8    8
    5
    3    0    3   48   48    8    8    5    5    0 9999    3    0    3    8    8
    5
    0    3    5   48   48    8    8    5    5    3    3 9999    3    5    8    8
    5
    3    0    3   48   48    8    8    5    5    0    0    3 9999    3    8    8
    5
    5    3    0   72   72   48   48   24   24    3    3    5    3 9999   48   48
   24
    8    8   50    6    6    0    0    8    8    8    8    8    8   50 9999    0
    8
    8    8   50    6    6    0    0    8    8    8    8    8    8   50    0 9999
    8
    5    5   26   12   12    8    8    0    0    5    5    5    5   26    8    8
 9999
EOF

我想提升矩阵的维度和矩阵本身,其他的都可以丢弃。这是我目前用来尝试解析它的代码:

fp = fopen(argv[1] , "r");

for (i = 0; i < 3; ++i)
{
    fscanf(fp, "\n");
}
fscanf(fp, "%d", &size);
for (i = 0; i < 3; ++i)
{
    fscanf(fp, "\n");
}
cost = (double**) calloc(size, sizeof(double*));
for(i = 0 ; i < size; ++i){
    cost[i] = (double*) calloc(size, sizeof(double));
}

for(i = 0 ; i < size; ++i)
{
    for(j = 0 ; j < size; ++j)
    {
        fscanf(fp, "%lf", &(cost[i][j]));
    }
    cost[i][i] = 0;
}

fclose(fp);

(当我在文本编辑器中打开该文件时,它似乎有换行符 - 虽然不是在记事本中 - 我不知道为什么它们在这里消失了。名称、类型、注释、尺寸、EDGE_WEIGHT_TYPE、EDGE_WEIGHT_FORMAT 和EDGE_WEIGHT_SECTION 都出现在新行的开头。编辑:啊,谢谢,Yossarian。我是 Stack Overflow 新手!)

无论如何,我的代码不起作用。具体来说,我通过使用调试器注意到它并没有提升矩阵的维度,这意味着正确读取矩阵的尝试从一开始就注定要失败。所有变量都已声明,这不是问题。它只是不读取尺寸后的数字并将其分配给尺寸。我做错了什么?

编辑:我已经尝试过 Vicky 对 fscanf(fp, "%s\n", buf); 的建议——它还有一个优点是让我可以通过观察 buf 的值来查看它在文件中的位置——并且发现它一次只占用一个单词,而不是一条线。这种方法的问题在于 COMMENT: 行的字数不一致。使用"%*s""%*s\n" 根本没有向buf 写入任何内容。

编辑 2:while((c = getchar()) != '\n' &amp;&amp; c != 'EOF') ; 只是挂起程序。不知道它在做什么。

编辑 3:while((c = getc(fp)) != '\n' &amp;&amp; c != 'EOF') ; 正在逐行浏览文件,但 fscanf(fp, "%d", &amp;size); 仍然没有收到号码。

编辑 4:啊哈!得到它的工作

char c; 
for (i = 0; i < 3; ++i)
{
    while((c = getc(fp)) != '\n' && c != 'EOF') ;
}
fscanf(fp, "%*s");
fscanf(fp, "%i", &size);

for (i = 0; i < 4; ++i)
{
    while((c = getc(fp)) != '\n' && c != 'EOF') ;
}

谢谢大家的帮助!

【问题讨论】:

  • 我不认为你可以通过fscanf(fp, "\n"); 来消费这样的一行,可以吗?我认为您需要fscanf(fp, "%s\n", buf);(首先适当地声明了buf)。
  • 另外,C 对于这种类型的文本处理来说是一种糟糕的语言——你可以选择使用 python、perl 或其他脚本语言吗?
  • 如果你想“跳过一行”,为什么不使用int c; while((c = getchar()) != '\n && c != EOF) ;` [让它成为一个名为“skipline”之类的函数]。
  • 有点矫枉过正。 fscanf(fp, "%*s"); 会做得更好。 (没有任何缓冲区)
  • 我更愿意使用 Python - 我实际上很胜任 - 但我需要更快的 C 速度来处理我对数据进行的计算,我不知道如何去连接C和Python。感谢您的指点,将尝试两种方法!

标签: c fileparsing


【解决方案1】:

我一直发现 scanf 函数带来的问题比它们解决的问题要多。

就个人而言,我更喜欢使用 fgets:-

char buffer [1024];
file = fopen (filename);

while (fgets (buffer, 1024, file))
{
  ParseState state = FindingLineType;

  for (char *token = strtok (buffer, " ") ; token ; token = strtok (0, " "))
  {
    // parse the token!
    switch (state)
    {
    case FindingLineType:
      if (stricmp (token, "DIMENSION:") == 0)
      {
        state = GettingDimension;
      }
      else
      {
        if (isdigit (*token))
        {
          if matrix has been created
          {
            state = ParsingMatrix;
          }
          else
          {
            error - got matrix row before dimension
          }
        }
      }
      break;

    case GettingDimension:
      dimension = atoi (token);
      create matrix
      break;
    }
  }
}

这应该会给你一些想法,你当然可以添加更多的错误检查。

【讨论】:

  • 我确信它工作得很好,但这远高于我的 C 能力水平!我不知道那个代码是什么意思!
  • @TamCoton:别担心,你最终会到达那里。有相当多的缺失(枚举/变量定义),而且不都是真正的 C,那里也有一些伪代码。
【解决方案2】:

仅依靠\n 来确定行尾是不可靠的,如果您的文件以\r 作为行终止符怎么办?事实上,您需要将\n\r\r\n 视为行结束序列。

Windows 记事本是一种“Hello World”编辑器。它非常基本且有限,只能处理 \r\n 作为行尾,这就是为什么如果行以 \r\n 结尾,它会显示一行。

C 中的行读取不是一项简单的任务,如果您知道一行的最大长度,您可以使用fgets,这样您就可以传递一个适当分配的缓冲区。否则,您将不得不处理未知的行长度,这就是COMMENT 行的情况。我个人更喜欢让女巫可以处理所有这些,并且只需在一次调用中从文件中返回一行,请参阅此类read_line 函数的示例。

在您提供的文件格式中,矩阵的大小出现在第 4 行。所以必须转义到前三行,只需执行以下三遍

read_line(pf);

在 4 日 read_line 你有包含大小的行。您需要提取并保存以供以后使用。

// DIMENSION:  17
line = read_line(pf);
printf("%s\n", line);
// extact the size of the matrix
tmp = strtok(line, " ");
tmp = strtok(NULL, " ");
size = atoi(tmp);
printf("Parsed size = %d\n", size);
free(line);
line_number++;

现在您的矩阵还剩下三行。像第一批一样逃离他们。

现在你的矩阵开始了,你可以在这里使用fscanf,你可以在分配矩阵行后立即开始从文件中读取,这样可以节省一些不必要的迭代。

以下是构建循环的方法:

if(size > 0) {
    printf("Start reaading matrix of size %dx%d\n\n", size, size);
    matrix = malloc(sizeof(double*) * size);
    for(i = 0; i < size; i++) {
        matrix[i] = malloc(sizeof(double) * size);

        n_read = 0;
        for(j = 0; j < size; j++) {
            n_read += fscanf(pf, "%lf", &matrix[i][j]);
            printf("%.2lf\t", matrix[i][j]);
        }
        printf("\n");
        line_number++;
        if(n_read != size) {
            printf("invalid data at line %d, expected to read %d but got %d\n", line_number, size, n_read);
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-23
    • 2010-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-25
    • 2018-06-04
    相关资源
    最近更新 更多