gml檔案格式解析程式詳解之原始檔
阿新 • • 發佈:2019-02-06
// 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); }