1. 程式人生 > >gml檔案格式解析程式詳解之原始檔

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 <stdlib.h>
#include <stdio.h>
#include <string.h>


#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->id<v2p->id) 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; i<network->nvertices; 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 (id<idsplit) {
      top = split;
      split = (top+bottom)/2;//其值要麼大,要麼小,要麼就只有等於。
    } else return split;
  } while (top>bottom);


  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; i<network->nvertices; 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; i<network->nvertices; i++) { //在釋放的時候,先釋放邊,在釋放標籤,最後釋放節點。
    free(network->vertex[i].edge);
    free(network->vertex[i].label);
  }
  free(network->vertex);
}