gml文件格式解析程序详解之源文件

// Functions to read a network stored in a GML file into a NETWORK struct
//
// Mark Newman  11 AUG 06
//
// To use this software, #include "readgml.h" at the head of your program
// and then call the following.
//
// Function calls:
//   int read_network(NETWORK *network, FILE *stream)
//     -- Reads a network from the FILE pointed to by "stream" into the
//        structure "network".  For the format of NETWORK structs see file
//        "network.h".  Returns 0 if read was successful.
//   void free_network(NETWORK *network)
//     -- Destroys a NETWORK struct again, freeing up the memory




// Inclusions


#include 
#include 
#include 


#include "network.h"


// Constants


#define LINELENGTH 1000


// Types


typedef struct line {
  char *str;
  struct line *ptr;
} LINE;


// Globals


LINE *first;
LINE *current;




// Function to read one line from a specified stream.  Return value is
// 1 if an EOF was encountered.  Otherwise 0.


int read_line(FILE *stream, char line[LINELENGTH])// line在这里为一个字符数组
{
  if (fgets(line,LINELENGTH,stream)==NULL) return 1;  //每次都读一行
  line[strlen(line)-1] = '\0';   // Erase the terminating NEWLINE
  return 0;
}




// Function to read in the whole file into a linked-list buffer, so that we
// can do several passes on it, as required to read the GML format
// efficiently


int fill_buffer(FILE *stream)
{
  int length;
  char line[LINELENGTH];
  LINE *previous;  //之前的指针


  if (read_line(stream,line)!=0) {
    first = NULL;                // Indicates empty buffer
    return 1;
  }
  length = strlen(line) + 1;
  first = malloc(sizeof(LINE));//first 为指向第一个节点的指针
  first->str = malloc(length*sizeof(char));
  strcpy(first->str,line);   //用于完成将读到到line字符串赋值到 LINE的链表中。


  previous = first;
  while (read_line(stream,line)==0) { //将GML文件的每一行存在以FIRST存放的链表中,而LINE结构体为链表的节点。
    length = strlen(line) + 1;           //而LINE结构体中的,str字符指针,用来连接读到到字符串,这里的加1是为了存放\0空字符。
    previous->ptr = malloc(sizeof(LINE));  
    previous = previous->ptr;
    previous->str = malloc(length*sizeof(char));
    strcpy(previous->str,line);
  }
  previous->ptr = NULL;          // Indicates last line  //将最后一个节点的ptr的指向为空,说明链表到了尾点


  return 0;
}




// Function to free up the buffer again


void free_buffer()
{
  LINE *thisptr;
  LINE *nextptr;


  thisptr = first;
  while (thisptr!=NULL) {
    nextptr = thisptr->ptr;
    free(thisptr->str);  //释放thisptr所指向的字符串
    free(thisptr);      //释放LINE节点
    thisptr = nextptr;  //将thisptr释放后,thisptr向前移
  }
}




// Function to reset to the start of the buffer again // 让目前指针,指向开始的位置


void reset_buffer()
{
  current = first;
}




// Function to get the next line in the buffer.  Returns 0 if there was
// a line or 1 if we've reached the end of the buffer.


int next_line(char line[LINELENGTH])
{
  if (current==NULL) return 1;  //current指针,指向当前的LINE节点,然后LINE节点的str指针所指向的字符串赋值到line的字符数组中
  strcpy(line,current->str);
  current = current->ptr;
  return 0;
}






// Function to establish whether the network read from a given stream is //判断这个网络是有向图还是无向图
// directed or not.  Returns 1 for a directed network, and 0 otherwise.  If
// the GML file contains no "directed" line then the graph is assumed to be
// undirected, which is the GML default behavior.    //如果GML图中,不包含有向的直线,那么我们认为其为无向图。


int is_directed()                                    //无向图同时也是GML默认的行为。
{
  int result=0;
  char *ptr;
  char line[LINELENGTH];


  reset_buffer();  //通常和net_line函数一起使用,因为它重置了current指针,可以再net_line中使用。


  while (next_line(line)==0) {
    ptr = strstr(line,"directed"); //strstr函数的作用,就是在line中查找derected这个字符串第一次出现的位置。
    if (ptr==NULL) continue;
    sscanf(ptr,"directed %i",&result);//以ptr所指向的字符串,作为sscanf语句的输入
    break;                           //并将其得到的数字按照十进制存储在result变量中
  }


  return result;              //只要是有向图,其directed后面一定会有数据的,这样result值不会为0,用以判断。
}




// Function to count the vertices in a GML file.  Returns number of vertices.
//怎么来查找node节点呢?首先找到"node ["标志,然后在同一行里面没有"label"标志,则将node节点的数目加1
int count_vertices()
{
  int result=0;
  char *ptr;
  char line[LINELENGTH];


  reset_buffer();  //这样也是重置current指针,用来计算节点的数目


  while (next_line(line)==0) { // next_line成功的时候返回的是0值。将LINE类型,名为current指针所指向节点的str返回所指向的那一行数据
    ptr = strstr(line,"node ["); //其中strstr函数是很有用的,相当于用来匹配。
    if (ptr!=NULL) {
      ptr = strstr(line,"label");//这个不太理解,其实所看到的gml格式的文件,不像是用txt格式打开的样子。但是从格式上看好像不会出现那个样子
      if (ptr==NULL) result++;  //如果找不到相应的字符串,则将指针置空,但是也不能计算节点的数目 
    }
  }


  return result;
}




// Function to compare the IDs of two vertices


int cmpid(VERTEX *v1p, VERTEX *v2p)
{
  if (v1p->id>v2p->id) return 1;    //比较两个节点的id,大于的话返回1,小于的话返回-1.
  if (v1p->idid) return -1;
  return 0;
}




// Function to allocate space for a network structure stored in a GML file
// and determine the parameters (id, label) of each of the vertices.


void create_network(NETWORK *network)
{
  int i;
  int length;
  char *ptr;
  char *start,*stop;
  char line[LINELENGTH];
  char label[LINELENGTH];


  // Determine whether the network is directed


  network->directed = is_directed();


  // Count the vertices


  network->nvertices = count_vertices();


  // Make space for the vertices


  network->vertex = calloc(network->nvertices,sizeof(VERTEX));// 运用calloc函数一下分配多个节点的空间,将所有的节点穿成链表。
                                                              //并将首地址 赋值给network的vertex指针。相当于构建了一个数组
  // Go through the file reading the details of each vertex one by one


  reset_buffer(); //这个函数调用的好及时啊,重新设置current指针。使其与first相同,共同指向第一个LINE节点
  for (i=0; invertices; i++) {


    // Skip to next "node" entry


    do {
      next_line(line);
    } while (strstr(line,"node")==NULL); //先读,然后判断。当寻到"node"时,说明这一行描述的为node节点。


    // Read in the details of this vertex


    do {                                //从上面可以看出,gml格式的话,


      // Look for ID //查询ID


      ptr = strstr(line,"id");
      if (ptr!=NULL) sscanf(ptr,"id %i",&network->vertex[i].id);


      // Look for label //查询label


      ptr = (strstr(line,"label"));   //这个循环写的好啊!充分体现了当node、id、label三者在不同的行的情况下如何处理。
      if (ptr!=NULL) { //标记处!!!和最外层的括号相对应。
      start = strchr(line,'"'); //在line中,strchr(s,c).在字符串s中,首次出现字符c的位置。
    if (start==NULL) {       //""的意思是出现"的地方,因为不能确定label是否是字符串,存在的话返回指向c的指针,不存在的话返回NULL
     sscanf(ptr,"label %s",&label);
} 
else {
   stop = strchr(++start,'"');//这样的话相对于start=strstr(line,"label")来说,只是起始位置发生了变化,原先为line字符数组的起始位置
   if (stop==NULL) length = strlen(line) - (start-line);         //现在为label标签项下的字符串的第一个字符。
   else length = stop - start;  //在这里做了一个判断,看一看其标签是否为一字符串,同时具有" ".
   strncpy(label,start,length);
   label[length] = '\0';
   network->vertex[i].label = malloc((length+1)*sizeof(char));//当label可以给length个元素赋值的时候,其元素的个数为length+1
   strcpy(network->vertex[i].label,label);//区分,一个label是属于自定义的,而另一个则属于vertex
}
      }//和标记处的if相对应。


      // If we see a closing square bracket we are done


      if (strstr(line,"]")!=NULL) break;  //这样的话已经找到结束的标志,结束就好了,同时也可以看到]应该和node[ 并不在一行中。


    } while (next_line(line)==0);//如果说ptr=NULL的话,那么就直接再读下一行


  }


  // Sort the vertices in increasing order of their IDs so we can find them
  // quickly later  //对节点进行排序,为后面的节点查找做准备。


  qsort(network->vertex,network->nvertices,sizeof(VERTEX),(void*)cmpid);
}




// Function to find a vertex with a specified ID using binary search.//使用二分查找,来查找给予特定ID的节点。
// Returns the element in the vertex[] array holding the vertex in question,
// or -1 if no vertex was found.  //找到的话返回这个节点,找不到的话就返回-1,二分查找的程序以后很可能会遇到。


int find_vertex(int id, NETWORK *network)
{
  int top,bottom,split;
  int idsplit;


  top = network->nvertices;
  if (top<1) return -1;
  bottom = 0;
  split = top/2; //节点的分割点,因为这个时候,链表已经按照节点的ID的大小已经排好序了,并且按照的是从小到大的顺序。


  do {
    idsplit = network->vertex[split].id;//split链表处所对应的ID为多少
    if (id>idsplit) {
      bottom = split + 1;
      split = (top+bottom)/2;//在这个时候top的值为发生变化,只是重新寻找split的位置。
    } else if (idbottom);


  return -1;
}
    


// Function to determine the degrees of all the vertices by going through
// the edge data


void get_degrees(NETWORK *network)//这里计算点的度数并没有计算权值
{
  int s,t;  // 这里的st,表示的source节点,与target目标节点。
  int vs,vt;
  char *ptr;
  char line[LINELENGTH];


  reset_buffer(); //重置current指针。


  while (next_line(line)==0) {


    // Find the next edge entry


    ptr = strstr(line,"edge");
    if (ptr==NULL) continue;  //直到遇到"edge"的字符串为止。


    // Read the source and target of the edge


    s = t = -1; //此时的请求,说明已经找到edge所在的行。


    do {


      ptr = strstr(line,"source");
      if (ptr!=NULL) sscanf(ptr,"source %i",&s);
      ptr = strstr(line,"target");
      if (ptr!=NULL) sscanf(ptr,"target %i",&t);


      // If we see a closing square bracket we are done


      if (strstr(line,"]")!=NULL) break;


    } while (next_line(line)==0); //这个的表示方式,和之前将节点的ID和节点的label基本上就是相同的,并充分考虑了第一次并未遇到source的情况


    // Increment the degrees of the appropriate vertex or vertices


    if ((s>=0)&&(t>=0)) { //在网络中,找到相应的ID所对应的VERTEX节点。并改变vertex节点的度数。
      vs = find_vertex(s,network);
      network->vertex[vs].degree++;
      if (network->directed==0) { //如果这个网络为无向图,则增加目标节点的度数。
    vt = find_vertex(t,network);
network->vertex[vt].degree++;
      }
    }//对应前面的两个if语句


  }//对应的是外层的while循环


  return;
}




// Function to read in the edges


void read_edges(NETWORK *network)
{
  int i;
  int s,t; //对应gml文件中的source节点,和target节点
  int vs,vt; //对应于NETWORK网络中的源节点与目标节点。
  int *count;
  double w;// 对应于权值,这里的类型设置的也不错
  char *ptr;
  char line[LINELENGTH];


  // Malloc space for the edges and temporary space for the edge counts 边计数
  // at each vertex


  for (i=0; invertices; i++) {
    network->vertex[i].edge = malloc(network->vertex[i].degree*sizeof(EDGE));//分配用于存储节点i所对应的边EDGE结构的题的空间的集合。
  }
  count = calloc(network->nvertices,sizeof(int));//分配n个int存储空间。并将分配的地址复制给count指针变量
                                                 //对应每一个节点所连接的边的数目,比如说count[12]=3;表示的是节点12有三条边相连。
  // Read in the data                            //这个值应该和节点的度数一直啊


  reset_buffer();//和前面的作用一样


  while (next_line(line)==0) {


    // Find the next edge entry


    ptr = strstr(line,"edge");
    if (ptr==NULL) continue;


    // Read the source and target of the edge and the edge weight


    s = t = -1;
    w = 1.0;


    do {


      ptr = strstr(line,"source"); //这里很简单的将source、target、value的值分别存于s,t,w这三个变量中。
      if (ptr!=NULL) sscanf(ptr,"source %i",&s);
      ptr = strstr(line,"target");
      if (ptr!=NULL) sscanf(ptr,"target %i",&t);
      ptr = strstr(line,"value");
      if (ptr!=NULL) sscanf(ptr,"value %lf",&w);


      // If we see a closing square bracket we are done


      if (strstr(line,"]")!=NULL) break;


    } while (next_line(line)==0);


    // Add these edges to the appropriate vertices


    if ((s>=0)&&(t>=0)) {   //在gml文件中,找到source节点与target节点,并找到两个节点之间多对应的权值。
      vs = find_vertex(s,network);
      vt = find_vertex(t,network);
      network->vertex[vs].edge[count[vs]].target = vt;//刚开始式子有点长,看起来很复杂。在确定vetex之后,对target进行复制。
      network->vertex[vs].edge[count[vs]].weight = w;//count[vs]并未有值啊?count[vs]为一个变量,起始值为0.edge[count[vs]]也只是一个EDGE的结构体
      count[vs]++;                                  //count[vs]的值在发生变化,最后记录的是某一个节点多对应的边的数目。
      if (network->directed==0) { //并将这些权值赋予NETWORK中的
network->vertex[vt].edge[count[vt]].target = vs;
network->vertex[vt].edge[count[vt]].weight = w;
count[vt]++;
      }
    }


  }


  free(count);
  return;
}




// Function to read a complete network


int read_network(NETWORK *network, FILE *stream)
{
  fill_buffer(stream);
  create_network(network);
  get_degrees(network);
  read_edges(network);
  free_buffer();


  return 0;
}




// Function to free the memory used by a network again


void free_network(NETWORK *network)
{
  int i;


  for (i=0; invertices; i++) { //在释放的时候,先释放边,在释放标签,最后释放节点。
    free(network->vertex[i].edge);
    free(network->vertex[i].label);
  }
  free(network->vertex);
}


你可能感兴趣的:(C语言之精深学习)