【问题标题】:Parsing fuction parses file but only partially解析功能解析文件但仅部分解析
【发布时间】:2020-11-27 16:23:24
【问题描述】:

我正在使用一种必须在 C 中解析的文件格式,如下所示:

5
3
1 3
1 4
1 5
3
2 3
2 4
2 5
2
3 1
3 2
3
4 1
4 2
4 5
3
5 1
5 2
5 4

除了第一行告诉我们顶点的数量外,每一行都有一个数字表示后面有多少行有2个数字。在这 2 条数字线上,我们可以看到在图中连接的 2 个顶点 ID。我的目标是获取这些信息并将 int 转换为图表。我编写了以下函数来读取单个顶点:

graph_vertex_t* read_vertex(FILE *fp, graph_vertex_t *last_vertex) 
{
    graph_vertex_t *current_vertex;
    
    graph_edge_t *tmp_edge;
    
    graph_edge_t *last_edge;
    
    char *token;
    
    boolean_t error = false;
    
    current_vertex = (graph_vertex_t *)malloc(sizeof(graph_vertex_t));
    
    int tmp_number_edges = 1;
    /*Temporary character string used to read line in file. */
    char *tmp;
    /*Allocating memory for string. Again I'm assuming 64 charcters is*/
    /*all I need. */
    tmp = malloc(sizeof(char) * 64);
    while (fgets(tmp, 
                 63, 
                 fp) != NULL 
                 &&
                 tmp_number_edges > 0)
    {   
        tmp[strcspn(tmp, "\n")] = 0;
        /* Check if the line contains a vertex number. */
        if (is_number(tmp))
        {
            /* Set the line to the vertex number. */
            tmp_number_edges = atoi(tmp);
        }
        else
        {           
            /* check if line has the right format. */
            token = strtok(tmp, " \t");
            if (is_number(token))
            {
                /* Set the identifier of the vertex. */
                current_vertex->identifier = atoi(token);
                current_vertex->next_vertex_p = last_vertex;
            }
            else
            {
                error = true;
            }
            
            token = strtok(NULL, " \t");
            if (is_number(token))
            {
                /* Make new edge. */
                tmp_edge = (graph_edge_t *)malloc(sizeof(graph_edge_t));
                tmp_edge->adjac_vertex_p = (graph_vertex_t *)malloc(sizeof(graph_vertex_t));
                tmp_edge->adjac_vertex_p->identifier = atoi(token);
                tmp_edge->next_edge_p = last_edge;              
                last_edge = tmp_edge;
                printf(" %d", tmp_edge->adjac_vertex_p->identifier);
            }
            else
            {
                error = true;
            }
            tmp_number_edges--;
        }
    }
    current_vertex->edge_list_p = tmp_edge;
    if(error)
    {
        current_vertex = NULL;
    }
    return(current_vertex);
}

这个函数在一个for循环中被调用,该循环循环计算顶点的数量。然后打印一些信息:

printf("connecting to: ");
            
current_vertex = read_vertex(fp,
                             last_vertex);
printf(" found vertex ID: %d \n", current_vertex->identifier);

因此,我希望输出看起来像这样:

连接到:3 4 5 找到顶点 ID:1
连接到:3 4 5 找到顶点 ID:2
连接到:1 2 找到顶点 ID:3
连接到:1 2 5 找到顶点 ID:4
连接到:1 2 4 找到顶点 ID:5

但实际上是这样的:

连接到:3 4 5 找到顶点 ID:1
连接到:3 找到的顶点 ID:2
连接到:5 找到的顶点 ID:2
连接到:1 找到顶点 ID:3
连接到:1 2 5 找到顶点 ID:4
连接到:1 找到的顶点 ID:5

我们可以看到,对于 ID 1 和 4,输出是正确的,但对于其余的则不是。是什么导致了这种行为?我该如何解决这个问题?

编辑:

这里是graph_vertex_t和graph_edge_t的定义。

* Structure used to hold a graph vertex information. */
typedef struct graph_vertex
{
    int identifier;
    struct graph_vertex *next_vertex_p;
    struct graph_edge *edge_list_p;
    boolean_t visited;
} graph_vertex_t;

/* Structure used to hold a graph edge information. */
typedef struct graph_edge
{
    struct graph_vertex *adjac_vertex_p;
    struct graph_edge *next_edge_p;
} graph_edge_t;

【问题讨论】:

  • 你的图有 5 个顶点,但你分配的远不止这些。当您阅读3 1 时,您无法立即分配顶点 1,您已经创建了该顶点。您想编写一个边创建例程,该例程分配一个顶点当且仅当图中没有具有该 ID 的顶点。
  • 另外,你的解析过于复杂。 atoi、tokens... 一次调用sscanf 就足够了。
  • 在每个子组中,第一个 id 总是相同的,是规则还是随机的?否则我无法理解子组的原因。我想在“1 3”之后“3 1”也是多余的
  • graph_vertex_t 和 graph_edge_t 是你定义的吗?顶点 ID 是否总是从 1 开始的连续数字?
  • @BRHSM 在这种情况下简化您的生活,通过指针或添加一个字段,说明当前条目中是否定义了顶点

标签: c string file parsing


【解决方案1】:

根据您的评论

我首先要了解sscanf

为了帮助您阅读这里,有一些建议,在您的结构中没有任何记忆

第一个提案没有检查数字是否在单独的行中:

#include <stdio.h>

int main()
{
  int nVertices, verticesRank;
  
  if ((scanf("%d", &nVertices) != 1) ||
      (nVertices < 0)) {
    fputs("wrong number of vertices\n", stderr);
    return -1;
  }

  for (verticesRank = 0; verticesRank < nVertices; verticesRank += 1) {
    int nGroups, groupRank, vertex;
    
    if ((scanf("%d", &nGroups) != 1) ||
        (nGroups < 0)) {
      fprintf(stderr, "wrong number of groups in vertices #%d\n", verticesRank);
      return -1;
    }
    
    for (groupRank = 0; groupRank < nGroups; groupRank += 1) {
      int a, b;
      
      if ((scanf("%d%d", &a, &b) != 2) ||
          (a < 1) || (b < 1)) {
        fprintf(stderr, "wrong vertex IDs group #%d vertices #%d\n",
               groupRank, verticesRank);
        return -1;
      }
      
      if (groupRank == 0) {
        vertex = a;
        printf("vertex %d connecting", a);
      }
      else if (a != vertex) {
        fprintf(stderr, "wrong vertex ID group #%d vertices #%d, %d rather than %d\n",
                groupRank, verticesRank, a, vertex);
        return -1;
      }
      printf(" %d", b);
    }
    putchar('\n');
  }
  
  return 0;
}

编译和执行:

pi@raspberrypi:/tmp $ gcc -Wall c.c
pi@raspberrypi:/tmp $ cat > v
5
3
1 3
1 4
1 5
3
2 3
2 4
2 5
2
3 1
3 2
3
4 1
4 2
4 5
3
5 1
5 2
5 4
pi@raspberrypi:/tmp $ ./a.out <v
vertex 1 connecting 3 4 5
vertex 2 connecting 3 4 5
vertex 3 connecting 1 2
vertex 4 connecting 1 2 5
vertex 5 connecting 1 2 4
pi@raspberrypi:/tmp $ 

第二个版本检查每行有正确数量的值:

#include <stdio.h>

int main()
{
  char line[64];
  int nVertices, verticesRank;
  int dummy;
  
  if ((fgets(line, sizeof(line), stdin) == NULL) ||
      (sscanf(line, "%d%d", &nVertices, &dummy) != 1) ||
      (nVertices < 0)) {
    fputs("wrong number of vertices\n", stderr);
    return -1;
  }

  for (verticesRank = 0; verticesRank < nVertices; verticesRank += 1) {
    int nGroups, groupRank, vertex;
    
    if ((fgets(line, sizeof(line), stdin) == NULL) ||
        (sscanf(line, "%d%d", &nGroups, &dummy) != 1) ||
        (nGroups < 0)) {
      fprintf(stderr, "wrong number of groups in vertices #%d\n", verticesRank);
      return -1;
    }
    
    for (groupRank = 0; groupRank < nGroups; groupRank += 1) {
      int a, b;
      
      if ((fgets(line, sizeof(line), stdin) == NULL) ||
          (sscanf(line, "%d%d%d", &a, &b, &dummy) != 2) ||
          (a < 1) || (b < 1)) {
        fprintf(stderr, "wrong vertex IDs group #%d vertices #%d\n",
               groupRank, verticesRank);
        return -1;
      }
      
      if (groupRank == 0) {
        vertex = a;
        printf("vertex %d connecting", a);
      }
      else if (a != vertex) {
        fprintf(stderr, "wrong vertex ID group #%d vertices #%d, %d rather than %d\n",
                groupRank, verticesRank, a, vertex);
        return -1;
      }
      printf(" %d", b);
    }
    putchar('\n');
  }
  
  return 0;
}

编译和执行:

pi@raspberrypi:/tmp $ gcc -Wall cc.c
pi@raspberrypi:/tmp $ ./a.out <v
vertex 1 connecting 3 4 5
vertex 2 connecting 3 4 5
vertex 3 connecting 1 2
vertex 4 connecting 1 2 5
vertex 5 connecting 1 2 4
pi@raspberrypi:/tmp $ 

注意我不检查是否有数字以外的东西,所以第一行被认为是正确的5aze,例如,如果你需要检查:

#include <stdio.h>

int main()
{
  char line[64];
  int nVertices, verticesRank;
  char dummy;
  
  if ((fgets(line, sizeof(line), stdin) == NULL) ||
      (sscanf(line, "%d%c", &nVertices, &dummy) != 2) ||
      (nVertices < 0) ||
      (dummy != '\n')) {
    fputs("wrong number of vertices\n", stderr);
    return -1;
  }

  for (verticesRank = 0; verticesRank < nVertices; verticesRank += 1) {
    int nGroups, groupRank, vertex;
    
    if ((fgets(line, sizeof(line), stdin) == NULL) ||
        (sscanf(line, "%d%c", &nGroups, &dummy) != 2) ||
        (nGroups < 0) ||
        (dummy != '\n')) {
      fprintf(stderr, "wrong number of groups in vertices #%d\n", verticesRank);
      return -1;
    }
    
    for (groupRank = 0; groupRank < nGroups; groupRank += 1) {
      int a, b;
      
      if ((fgets(line, sizeof(line), stdin) == NULL) ||
          (sscanf(line, "%d%d%c", &a, &b, &dummy) != 3) ||
          (a < 1) || (b < 1) ||
          (dummy != '\n')) {
        fprintf(stderr, "wrong vertex IDs group #%d vertices #%d\n",
               groupRank, verticesRank);
        return -1;
      }
      
      if (groupRank == 0) {
        vertex = a;
        printf("vertex %d connecting", a);
      }
      else if (a != vertex) {
        fprintf(stderr, "wrong vertex ID group #%d vertices #%d, %d rather than %d\n",
                groupRank, verticesRank, a, vertex);
        return -1;
      }
      printf(" %d", b);
    }
    putchar('\n');
  }
  
  return 0;
}

编译和执行:

pi@raspberrypi:/tmp $ gcc -Wall cc.c
pi@raspberrypi:/tmp $ ./a.out <v
vertex 1 connecting 3 4 5
vertex 2 connecting 3 4 5
vertex 3 connecting 1 2
vertex 4 connecting 1 2 5
vertex 5 connecting 1 2 4
pi@raspberrypi:/tmp $ 

请注意,在这种情况下,即使最后一个预期数字之后的额外空格(换行符之外)也被视为错误,并且最后一行也必须以换行符结束。

那不检查理论结束后有什么东西,你可以通过几种方式来做。

在您的示例中,作为每对中第一个数字的顶点 ID 是连续数字 1、2 ...,在我之前的提案中,我不检查即使很容易检查 vertex == verticesRank+1。如果这是一个规则,为什么只有第二个数字就足够了,为什么要给这两个值呢?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-30
    • 1970-01-01
    • 1970-01-01
    • 2020-04-07
    • 2018-10-22
    • 2014-04-21
    • 1970-01-01
    相关资源
    最近更新 更多