图结构邻接表表示法的C语言实现代码

图结构邻接表表示法的C语言实现代码,图结构相关知识可以参考“图结构

https://www.cnblogs.com/value-code/articles/9206581.html”这篇文章。刚进入程序会默认从inputArc.csv读取边数据,从inputVex.csv读取节点数据。如果没有读到数据则会打开比网页。可以通过配置文件(config.ini)来更改程序的输入输出文件等。
操作说明(方便手机九宫格键盘操作):

1:查找 | 2:向上 | 3:上关联

4:向左 | 5:确认 | 6:向右

7:删除 | 8:向下 | 9:下关联

,:修改 | 0:退出 | Ctrl+E:进入管理界面

查看代码

/*
* 文件名称: graph.c
* 摘要: 数据结构图
* 当前版本:
* 历史版本:
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <time.h>
#include <stdarg.h>
#include <assert.h>
// 取消注释禁用 assert()
// #define NDEBUG
#define CTRL_KEY(k) ((k) & 0x1f)
#define MAX_VERTEX_NUM 80000  //存储节点的上限
#define Length_limit 6000   //限制单次输入字符串最大限制
#define MAX_ARC_NUM 1000000  //限制最大链接数量
#define max_num 128
#define readSize 1024

typedef struct dual_Node {
  int element;
  struct dual_Node *pripor, *next;
}dualNode;
typedef struct {
  dualNode *top, *bottom;
}links;
typedef struct {
  char *data[3];
  links *nextArc;
  links *priporArc;
}VNode;
typedef struct {
  int id[MAX_VERTEX_NUM];
  VNode *vertices[MAX_VERTEX_NUM];
  int vexnum;
  int arcnum;
  int endnum;
}ALGraph;

signed int getInt(void);
links *initLinks(void);
int pushStart(links *linkPoint, int value);
int popStart(links *linkPoint);
int popEnd(links *linkPoint);
int lengthLinks(links *linkPoint);
int delElem(links *linkPoint, int value);
int searchElem(links *linkPoint, int value);
void destroyLinks(links **linkPoint);
char* dynamicStr(links *head);
ALGraph* createGraph(void);
void destroyGraph(ALGraph **graph);
int binarySearch(ALGraph *graph, char *str);
int binaryInsert(ALGraph *graph, char *value);
int insertVex(ALGraph *graph, char *value);
int insertArc(ALGraph *graph, int source, int dest);
void deleteArc(ALGraph *graph, int source, int dest);
void deleteVex(ALGraph *graph, int del);
int dispGraph(ALGraph *graph);
int readALG(ALGraph *graph, char *filename, int flag);
void saveCSV(char *str, FILE *fp);
int saveALG(ALGraph *graph, char *filename, int flag);
void Next(char *T, int *next);
int KMP(ALGraph *graph, char *T);
char* getKeyValu(char *filename, char *section, char *key);
int utf8ToUnicode(char *utf8, unsigned long *unicode);
int writeLog(const char *format, ...);

int main(void)
{
  ALGraph *digraph = createGraph();
  int opt = '5';
  char *menuList[] = {"Ctrl_E退出", "读取文件", "保存文件"};
  char fileName[512] = {0};
  if( digraph != NULL ){
    strcpy(fileName, getKeyValu("config.ini", "[file]", "inputVex.csv"));
    readALG(digraph, fileName, 0);
    strcpy(fileName, getKeyValu("config.ini", "[file]", "inputArc.csv"));
    readALG(digraph, fileName, 1);
    if( digraph->vexnum == 0 )
      system("start https://www.cnblogs.com/pangea/p/14858443.html");
    do {
      switch( opt ){
      case '1':
        strcpy(fileName, getKeyValu("config.ini", "[file]", "addArc.csv"));
        readALG(digraph, fileName, 1);
        strcpy(fileName, getKeyValu("config.ini", "[file]", "addVex.csv"));
        readALG(digraph, fileName, 0);
        break;
      case '2':
        strcpy(fileName, getKeyValu("config.ini", "[file]", "outputArc.csv"));
        saveALG(digraph, fileName, 1);
        strcpy(fileName, getKeyValu("config.ini", "[file]", "outputVex.csv"));
        saveALG(digraph, fileName, 0);
        break;
      default :
        opt = dispGraph(digraph);
        break;
      }
      if( opt == '0' )
        break;
      srand(time(0));
      sprintf(fileName, "%d", rand()%1350);
      strcpy(fileName, getKeyValu("soup.ini", "[soup]", fileName));
      printf("%s\n>", fileName);
      printf("总节点: %d\n总连接: %d\n", digraph->vexnum, digraph->arcnum);
      for(int i=0; i < 3; i++) {
        printf("%d、%s\n",i, menuList[i]);
      }
      fprintf(stdout, ">");
      opt = getInt();
    }while( opt != CTRL_KEY('e') );
    destroyGraph(&digraph);
  }
  return 0;
}
/*
* 函数介绍: 从 ini 文件中读取配置信息
* 参数:
* 返回值: 
* 备注: 以 # 开头的为注释行,每行长度不得超过 512 个字符
*/
char* getKeyValu(char *filename, char *section, char *key)
{
  char line[512] = {0};
  char sectname[512] = {0};
  char *skey = NULL, *valu = NULL;
  char seps[] = "=";
  bool flag = false, find = false;
  FILE *fp = fopen(filename, "r");
  if( fp == NULL ){
    return key;
  }
  if( strchr(section, '[') == NULL){
    strcpy(sectname, "[");
    strcat(sectname, section);
    strcat(sectname, "]");
  } else {
    strcpy(sectname, section);
  }
  while( fgets(line, 512, fp) != NULL ){
    if( strcmp(line, "\n") == 0 ) { // 跳过空行
      continue;
    }
    if( (valu = strchr(line, '\r')) != NULL ){
      *valu = '\0';
    } else if( (valu = strchr(line, '\n')) != NULL ){
      *valu = '\0';
    } else {  // 跳过超过长度的行
      while( fgets(line, 512, fp) != NULL ){
        if( (valu = strchr(line, '\r')) != NULL ){
          break;
        } else if( (valu = strchr(line, '\n')) != NULL ){
          break;
        }
      }
      continue;
    }
    if( line[0] == '#' ) { // 跳过注释
      continue;
    }
    if( line[0] == '[' ){ // 以 [ 开头的行默认为“节”
      if( find == true ) {
        break;
      } else if( strcmp(sectname, line) == 0 ){
        find = true;
        flag = true;
      }
    }
    if( flag ){
      skey = strtok(line, seps);
      if( strcmp(skey, key) == 0 ){
        fclose(fp);
        return strtok(NULL, "\0");
      }
    }
  }
  fclose(fp);
  return key;
}
/*
* 函数介绍: 限制只接收一个字符的输入
* 参数:
* 返回值: 返回一个字符的数值
* 备注: 用于检测输入的合法性
*/
int getInt(void)
{
  int opt = 0;
  do {
    opt = getchar();
    if( opt == '\n' ){
      break;
    }
    if( getchar() == '\n' ){
      break;
    }
    while( getchar() != '\n' );
    printf("只能输入一个字符!\n");
  }while( 1 );
  return opt;
}
/*
* 函数介绍: 初始化双链表
* 参数:
* 返回值: 成功时,返回指向新分配内存的指针;失败时,返回空指针 NULL。
* 备注:
*/
links *initLinks(void)
{
  links *node = (links *)malloc(sizeof(links));
  if( node != NULL ){
    node->top = (dualNode *)malloc(sizeof(dualNode));
    if( node->top != NULL ){
      node->top->pripor = node->top;
      node->top->next = node->top;
      node->bottom = node->top;
      node->bottom->element = 0;
    } else {
      free(node);
      node = NULL;
    }
  }
  return node;
}
/*
* 函数介绍: 从头部插入数值
* 参数: link 指针,int 类型的元素
* 返回值: 成功时,返回0;失败时,返时1。
* 备注: 当没有更多的内存可分配时失败
*/
int pushStart(links *linkPoint, int value)
{
  assert( linkPoint != NULL );
  if( linkPoint->bottom->next == linkPoint->top ){
    dualNode *newElement = (dualNode *)malloc(sizeof(dualNode));
    if( newElement != NULL) {
      linkPoint->bottom->next = newElement;
      newElement->pripor = linkPoint->bottom;
      newElement->next = linkPoint->top;
      linkPoint->top->pripor = newElement;
    } else {
      writeLog("%s, %s, %d:内存错误\n", __FILE__, __func__, __LINE__);
      return 1;
    }
  }
  linkPoint->top = linkPoint->top->pripor;
  linkPoint->top->element = value;
  linkPoint->bottom->element++;
  return 0;
}
/*
* 函数介绍: 从头部弹出元素值
* 参数: link 指针
* 返回值: 链表为空时返回 -1
* 备注: 不检测链表中的值是否为 -1
*/
int popStart(links *linkPoint)
{
  assert( linkPoint != NULL );
  int resoult = -1;
  if( linkPoint->top != linkPoint->bottom ){
    resoult = linkPoint->top->element;
    linkPoint->top = linkPoint->top->next;
    linkPoint->bottom->element--;
  }
  return resoult;
}
/*
* 函数介绍: 从尾部弹出元素值
* 参数: link 指针
* 返回值: 链表为空时返回 -1
* 备注: 不检测链表中的值是否为 -1
*/
int popEnd(links *linkPoint)
{
  assert( linkPoint != NULL );
  int resoult = -1; 
  if( linkPoint->top != linkPoint->bottom ){
    resoult = linkPoint->bottom->pripor->element;
    linkPoint->bottom = linkPoint->bottom->pripor;
    linkPoint->bottom->element = linkPoint->bottom->next->element - 1;
  }
  return resoult;
}
/*
* 函数介绍: 计算链表的长度
* 参数: link 指针
* 返回值: 返回链表的长度
* 备注:
*/
int lengthLinks(links *linkPoint)
{
  assert( linkPoint );
  int length = 0;
  for( dualNode *point = linkPoint->top; point != linkPoint->bottom; length++ ) {
    point = point->next;
  }
  assert( length == linkPoint->bottom->element );
  return length;
}
/*
* 函数介绍: 删除链表中第一个value值
* 参数: link 指针,要删除的值
* 返回值: 删除成功返回 0,没有删除返回 -1。
* 备注: 
*/
int delElem(links *linkPoint, int value)
{
  assert( linkPoint != NULL );
  int flag = -1;
  dualNode *tmp = linkPoint->top;
  while( tmp != linkPoint->bottom ){
    if( tmp->element == value ){
      if( tmp ==  linkPoint->top ){
         linkPoint->top = linkPoint->top->next;
      } else {
        tmp->pripor->next = tmp->next;
        tmp->next->pripor = tmp->pripor;
        linkPoint->top->pripor->next = tmp;
        tmp->pripor = linkPoint->top->pripor;
        linkPoint->top->pripor = tmp;
        tmp->next = linkPoint->top;
      }
      linkPoint->bottom->element--;
      flag = 0;
    }
    tmp = tmp->next;
  }
  return flag;
}
/*
* 函数介绍: 查找链表中是否存在值value
* 参数:
* 返回值: 返回value在链表中的第几位,0代表未找到value值
* 备注:
*/
int searchElem(links *linkPoint, int value)
{
  assert( linkPoint != NULL);
  int num = 0;
  dualNode *tmp = linkPoint->top;
  while( tmp != linkPoint->bottom ){
    num++;
    if( tmp->element == value )
      return num;
    tmp = tmp->next;
  }
  return 0;
}
/*
* 函数介绍: 销毁链表
* 参数:
* 返回值:
* 备注: 
*/
void destroyLinks(links **linkPoint)
{
  if( *linkPoint != NULL ){
    (*linkPoint)->bottom = (*linkPoint)->top->next;
    (*linkPoint)->top->next = NULL;
    do {
      (*linkPoint)->top = (*linkPoint)->bottom;
      (*linkPoint)->bottom = (*linkPoint)->bottom->next;
      free((*linkPoint)->top);
    }while( (*linkPoint)->bottom != NULL );
    free(*linkPoint);
    *linkPoint = NULL;
  }
}
/*
* 函数介绍: 输入长度不超过Length_limit字节的字符串
* 参数:
* 返回值: 成功时,返回指向新分配内存的指针;失败时,返回空指针。
* 备注: 
*/
char* dynamicStr(links *head)
{
  int Bound = 0;
  int ch = getchar();
  head->top = head->bottom;
  head->bottom->element = 0;
  while( ch != '\n' && ch != '\0' && Bound <= Length_limit ){
    if( pushStart(head, ch) == 1 ){ // 没有内存可以分配了
      head->top = head->bottom;
      head->bottom->element = 0;
      return NULL;
    }
    Bound++;
    ch=getchar();
  }
  if( ch != '\n') { // 清空输入缓存
    while( getchar() != '\n' );
  }
  char *str = (char *)malloc((Bound+1) * sizeof(char));
  if( str != NULL ){
    for(int i=0; i < Bound; i++) {
      str[i] = popEnd(head);
    }
    str[Bound] = '\0';
  } else {
    head->top = head->bottom;
    head->bottom->element = 0;
  }
  return str;
}
/*
* 函数介绍: 初始化图存储结构
* 参数: 空
* 返回值: 成功时,返回已初始化的 ALGraph 指针;失败时,返回空指针。
* 备注:
*/
ALGraph* createGraph(void)
{
  ALGraph *graph = (ALGraph *)malloc(sizeof(ALGraph));
  if( graph != NULL ){
    for( int i=0; i < MAX_VERTEX_NUM; i++ ){
      graph->vertices[i] = NULL;
      graph->id[i] = 0;
    }
    graph->vexnum = 0;
    graph->arcnum = 0;
    graph->endnum = MAX_VERTEX_NUM;
  }
  return graph;
}
/*
* 函数介绍: 销毁图
* 参数:
* 返回值:
* 备注: 释放图结构中所有申请的内存
*/
void destroyGraph(ALGraph **graph)
{
  if(*graph != NULL) {
    for( int i=0; i < (*graph)->vexnum; i++ ) {
      destroyLinks(&(*graph)->vertices[(*graph)->id[i]]->nextArc);
      destroyLinks(&(*graph)->vertices[(*graph)->id[i]]->priporArc);
      for( int j=0; j < 3; j++ ){
        free((*graph)->vertices[(*graph)->id[i]]->data[j]);
      }
      free((*graph)->vertices[(*graph)->id[i]]);
    }
    free(*graph);
    *graph = NULL;
  }
}
/*
* 函数介绍: 精确查找元素在图中的位置,二分查找
* 参数:
* 返回值: 找到,返回元素在图中的索引位置;找不到,返回-1。
* 备注:
*/
int binarySearch(ALGraph *graph, char *str)
{
  int low = 0;
  int mid = 0;
  int high = 0;
  if( graph->vexnum > 0 ){
    high = graph->vexnum - 1;
    while( low <= high ){
      mid = low + (high - low)/2;
      if( strcmp(graph->vertices[graph->id[mid]]->data[0], str) == 0 )
        return mid;
      if( strcmp(graph->vertices[graph->id[mid]]->data[0], str) < 0 ){
        low = mid + 1;
      } else {
        high = mid - 1;
      }
    }
  }
  return -1;
}
/*
* 函数介绍: 二分插入
* 参数:
* 返回值:
* 备注:
*/
int binaryInsert(ALGraph *graph, char *value)
{
  int left = 0;
  int mid = 0;
  int right = 0;
  right = graph->vexnum - 1;
  while( left < right ){
    mid = left + (right - left)/2;
    if( strcmp(graph->vertices[graph->id[mid]]->data[0], value) < 0 ){
      left = mid + 1;
    } else {
      right = mid;
    }
  }
  return left;
}
/*
* 函数介绍: 在图中添加新节点
* 参数:
* 返回值: 成功,返回元素在图中的索引位置。
* 备注: -1出错,MAX_VERTEX_NUM已达到图存储极限
*/
int insertVex(ALGraph *graph, char *value)
{
  if( graph == NULL || value == NULL || *value == '\0' )
    return -1;
  if( graph->vexnum >= MAX_VERTEX_NUM )
    return MAX_VERTEX_NUM;
  int source = 0;
  int status = binarySearch(graph, value);
  if( status == -1 ){
    if( graph->endnum < MAX_VERTEX_NUM ){
      status = graph->id[graph->endnum];
      graph->endnum++;
    } else {
      status = graph->vexnum;
    } 
    graph->vertices[status] = (VNode *)malloc(sizeof(VNode));
    assert(graph->vertices[status]);
    graph->vertices[status]->data[0] = value;
    graph->vertices[status]->data[1] = NULL;
    graph->vertices[status]->data[2] = NULL;
    graph->vertices[status]->nextArc = initLinks();
    graph->vertices[status]->priporArc = initLinks();
    graph->vexnum++;
    source = binaryInsert(graph, value);
    for(int i = graph->vexnum - 1; i > source; i--) {
      graph->id[i] = graph->id[i-1];
    }
    graph->id[source] = status;
    status = source;
    value = NULL;
  }
  return status;
}
/*
* 函数介绍: 向图中添加一条弧
* 参数:
* 返回值: 成功,返回0;失败,返回-1
* 备注: 当达到极限时将不再新增,以此来限制程序对存储空间的使用。
*/
int insertArc(ALGraph *graph, int source, int dest)
{
  assert( graph != NULL);
  if( graph->arcnum > MAX_ARC_NUM || source == -1 || dest == -1 || source == dest )
    return -1;
  if( searchElem(graph->vertices[graph->id[source]]->nextArc, graph->id[dest]) == 0 ){
    pushStart(graph->vertices[graph->id[source]]->nextArc, graph->id[dest]);
    pushStart(graph->vertices[graph->id[dest]]->priporArc, graph->id[source]);
    graph->arcnum++;
  }
  return 0;
}
/*
* 函数介绍: 删除图中的一条弧
* 参数:
* 返回值:
* 备注:
*/
void deleteArc(ALGraph *graph, int source, int dest)
{
  assert( graph );
  if( graph->vertices[source] != NULL && graph->vertices[dest] != NULL ){
    if( delElem(graph->vertices[source]->nextArc, dest) == 0 ){
      delElem(graph->vertices[dest]->priporArc, source);
      graph->arcnum--;
    }
  }
}
/*
* 函数介绍: 删除指定顶点
* 参数:
* 返回值:
* 备注: 释放该节点下申请的内存
*/
void deleteVex(ALGraph *graph, int del)
{
  assert( graph );
  if( del != -1 ){
    int source = graph->id[del], dest=0;
    while( lengthLinks(graph->vertices[source]->nextArc) != 0 ){
      dest = graph->vertices[source]->nextArc->top->element;
      deleteArc(graph, source, dest);
    }
    destroyLinks(&graph->vertices[source]->nextArc);
    while( lengthLinks(graph->vertices[source]->priporArc) != 0 ){
      dest = graph->vertices[source]->priporArc->top->element;
      deleteArc(graph, dest, source);
    }
    destroyLinks(&graph->vertices[source]->priporArc);
    for( int i=0; i < 3; i++ ){
      free(graph->vertices[source]->data[i]);
    }
    free(graph->vertices[source]);
    graph->vertices[source] = NULL;
    for( int i = del; i < graph->vexnum - 1; i++ )
      graph->id[i] = graph->id[i+1];
    graph->vexnum--;
    graph->endnum--;
    graph->id[graph->endnum] = source;
  }
}
/*
* 函数介绍: 显示图中顶点
* 参数:
* 返回值:
* 备注:
*/
int dispGraph(ALGraph *graph)
{
  enum
  {
    UP  = '2',
    DOWN = '8',
    LEFT = '4',
    RIGHT = '6',
    SELECT = '5',
    PREVIOUS = '3',
    NEXT = '9',
    FIND= '1',
    CHANGE = ',',
    DELETE = '7',
    EXIT = '0',
    DISP = '.'
  };
  char *utf8 = NULL;
  unsigned long unicode = 0;
  int source = 0, dest = 0, current = 0;
  char string[max_num] = {0};
  char fmt[10] = {0};
  char *str = NULL;
  dualNode *temp = NULL;
  links *point = NULL;
  dualNode *vernier = NULL;
  int ch = FIND, loop = 0, lens = 0;
  links *head = initLinks();
  if( graph->vexnum == 0 ){
    printf("首个节点>");
    sprintf(fmt, "%%%ds", max_num - 1);
    do {
      memset(string, 0, sizeof(string));
    }while( scanf(fmt, string) != 1);
    while( getchar() != '\n' );
    str = (char *)malloc(strlen(string) + 1);
    strcpy(str, string);
    current = insertVex(graph, str);
    for( int j=1; j < 3; j++ ){
      printf("第 %d 项 >", j);
      graph->vertices[graph->id[current]]->data[j] =  dynamicStr(head);
    }
  }
  srand(time(NULL));
  current = rand()%graph->vexnum;
  point = graph->vertices[graph->id[current]]->priporArc;
  vernier = point->top;
  do {
    printf("———————————————————\n");
    printf("%s;%s", graph->vertices[graph->id[current]]->data[0], \
        graph->vertices[graph->id[current]]->data[1]);
    if( vernier != point->bottom ){
      printf("\n%s;%s", graph->vertices[vernier->element]->data[0], \
          graph->vertices[vernier->element]->data[1]);
    }
    printf("\n———————————————————");
    temp = graph->vertices[graph->id[current]]->priporArc->top;
    loop = graph->vertices[graph->id[current]]->priporArc->bottom->element;
    if( loop > 0 )
      printf("\n%d:", loop);
    while( loop > 0 ){
      if( temp == vernier ){
        fprintf(stdout, "%s《", graph->vertices[temp->element]->data[0]);
      } else if( vernier != graph->vertices[graph->id[current]]->priporArc->bottom
          && temp->element == vernier->element ){
        fprintf(stdout, "%s〈", graph->vertices[temp->element]->data[0]);
      } else {
        fprintf(stdout, "%s", graph->vertices[temp->element]->data[0]);
        if( loop > 1 )
          printf(",");
      }
      temp = temp->next;
      loop--;
    }
    printf("\n【%s】", graph->vertices[graph->id[current]]->data[0]);
    utf8 = graph->vertices[graph->id[current]]->data[0];
    loop = strlen(utf8);
    lens = 0;
    do {
      utf8 = utf8 + lens;
      lens = utf8ToUnicode(utf8, &unicode);
      loop = loop - lens;
      printf(" %lx", unicode);
    }while( loop > 0 && lens != 0 && *utf8 != '\0' );
    temp = graph->vertices[graph->id[current]]->nextArc->top;
    loop=graph->vertices[graph->id[current]]->nextArc->bottom->element;
    if( loop > 0 )
      printf("\n%d:", loop);
    while( loop > 0 ){
      if( temp == vernier ){
        fprintf(stdout, "%s《", graph->vertices[temp->element]->data[0]);
      } else if( vernier != graph->vertices[graph->id[current]]->priporArc->bottom
          && temp->element == vernier->element ){
        fprintf(stdout, "%s〈", graph->vertices[temp->element]->data[0]);
      } else {
        fprintf(stdout, "%s", graph->vertices[temp->element]->data[0]);
        if( loop > 1 )
          printf(",");
      }
      temp = temp->next;
      loop--;
    }
    printf("\n>");
    ch = getInt();
    switch( ch ){
      case UP:
        point = graph->vertices[graph->id[current]]->priporArc;
        vernier = point->top;
        break;
      case DOWN:
        point = graph->vertices[graph->id[current]]->nextArc;
        vernier = point->top;
        break;
      case LEFT:
        if( vernier == point->top ){
          vernier = point->bottom->pripor;
        } else {
          vernier = vernier->pripor;
        }
        break;
      case RIGHT:
        if( vernier->next == point->bottom ){
          vernier = point->top;
        } else {
          vernier = vernier->next;
        }
        break;
      case SELECT:
        if( vernier != point->bottom ){
          current = binarySearch(graph, graph->vertices[vernier->element]->data[0]);
          point = graph->vertices[graph->id[current]]->priporArc;
          vernier = point->bottom;
        }
        break;
      case PREVIOUS:
        fprintf(stdout, "反向链接:");
        sprintf(fmt, "%%%ds", max_num - 1);
        do {
          memset(string, 0, sizeof(string));
        }while( scanf(fmt, string) != 1 );
        while( getchar() != '\n' );
        dest = binarySearch(graph, string);
        if( dest == -1 ){
          if( graph->vexnum >= MAX_VERTEX_NUM ){
            printf("已达存储上限,无法新增\n");
          } else {
            printf("按'%c'增加:%s\n>", SELECT, string);
            if( getInt() == SELECT ){
              source = graph->id[current];
              str = (char *)malloc(strlen(string) + 1);
              strcpy(str, string);
              dest = insertVex(graph, str);
              for( int j=1; j < 3; j++ ){
                printf("第 %d 项: ", j);
                str = dynamicStr(head);
                if( *str != '\0' ){
                  graph->vertices[graph->id[dest]]->data[j] = str;
                } else {
                  free(str);
                }
                str = NULL;
              }
              current = binarySearch(graph, graph->vertices[source]->data[0]);
              insertArc(graph, dest, current);
            }
          }
        } else {
          insertArc(graph, dest, current);
        }
        break;
      case NEXT: fprintf(stdout, "插入链接:");
        sprintf(fmt, "%%%ds", max_num - 1);
        do {
          memset(string, 0, sizeof(string));
        }while( scanf(fmt, string) != 1 );
        while( getchar() != '\n' );
        dest = binarySearch(graph, string);
        if( dest == -1 ){
          if( graph->vexnum >= MAX_VERTEX_NUM ){
            printf("已达存储上限,无法新增\n");
          } else {
            printf("按'%c'增加:%s\n>", SELECT, string);
            if( getInt() == SELECT ){
              source = graph->id[current];
              str = (char *)malloc(strlen(string) + 1);
              strcpy(str, string);
              dest = insertVex(graph, str);
              for( int j=1; j < 3; j++ ){
                printf("第 %d 项: ", j);
                str = dynamicStr(head);
                if( *str != '\0' ){
                  graph->vertices[graph->id[dest]]->data[j] = str;
                } else {
                  free(str);
                }
                str = NULL;
              }
              current = binarySearch(graph, graph->vertices[source]->data[0]);
              insertArc(graph, current, dest);
            }
          }
        } else {
          insertArc(graph, current, dest);
        }
        break;
      case FIND:
        fprintf(stdout, "查找:");
        sprintf(fmt, "%%%ds", max_num - 1);
        do {
          memset(string, 0, sizeof(string));
        }while( scanf(fmt, string) != 1 );
        while( getchar() != '\n' );
        source = binarySearch(graph, string);
        if( source != -1 ){
          current = source;
          point = graph->vertices[graph->id[current]]->priporArc;
          vernier = point->top;
        } else {
          source = KMP(graph, string);
          if( source != MAX_VERTEX_NUM ){
            current = source;
            point = graph->vertices[graph->id[current]]->priporArc;
            vernier = point->top;
          }
        }
        break;
      case DELETE:
        if( point->bottom != vernier ){
          temp = vernier->next;
          if( point == graph->vertices[graph->id[current]]->priporArc ){
            deleteArc(graph, vernier->element, graph->id[current]);
          } else if( point == graph->vertices[graph->id[current]]->nextArc ){
            deleteArc(graph, graph->id[current], vernier->element);
          }
          if( temp != point->bottom ){
            vernier = temp;
          } else if( temp->element != 0 ){
            vernier = temp->pripor;
          } else if( point == graph->vertices[graph->id[current]]->priporArc ){
            point = graph->vertices[graph->id[current]]->nextArc;
            vernier = point->top;
          } else {
            point = graph->vertices[graph->id[current]]->priporArc;
            vernier = point->top;
          }
        } else {
          printf("按'%c'删除:%s\n>", SELECT, graph->vertices[graph->id[current]]->data[0]);
          if( getInt() == SELECT && graph->vexnum > 1 ){
            deleteVex(graph, current);
            current = 0;
            point = graph->vertices[graph->id[current]]->priporArc;
            vernier = point->top;
          }
        }
        break;
      case CHANGE:
        printf("修改\n");
        printf("%s\n>", graph->vertices[graph->id[current]]->data[0]);
        str = dynamicStr(head);
        if( binarySearch(graph, str) == -1 && *str != '\0' ){
          free(graph->vertices[graph->id[current]]->data[0]);
          graph->vertices[graph->id[current]]->data[0] = str;
          for( int j=1; j < 3; j++ ){
            printf("%s\n>", graph->vertices[graph->id[current]]->data[j]);
            str = dynamicStr(head);
            if( *str != '\0' ){
              free(graph->vertices[graph->id[current]]->data[j]);
              graph->vertices[graph->id[current]]->data[j] = str;
            } else {
              free(str);
            }
          }
          for( ; current < graph->vexnum-1; current++ ){
            if( strcmp(graph->vertices[graph->id[current]]->data[0], \
                  graph->vertices[graph->id[current+1]]->data[0]) > 0 ){
              source = graph->id[current];
              graph->id[current] = graph->id[current+1];
              graph->id[current+1] = source;
            } else {
              break;
            }
          }
          for( ; current > 0; current-- ){
            if( strcmp(graph->vertices[graph->id[current]]->data[0], \
                  graph->vertices[graph->id[current-1]]->data[0]) < 0 ){
              source = graph->id[current];
              graph->id[current] = graph->id[current-1];
              graph->id[current-1] = source;
            } else {
              break;
            }
          }
        } else {
          if( *str == '\0' ){
          free(str);
          for( int j=1; j < 3; j++ ){
            printf("%s\n>", graph->vertices[graph->id[current]]->data[j]);
            str = dynamicStr(head);
            if( *str != '\0' ){
              free(graph->vertices[graph->id[current]]->data[j]);
              graph->vertices[graph->id[current]]->data[j] = str;
            } else {
              free(str);
            }
          }
          } else {
            free(str);
          }
        }
        break;
      case DISP:
        for( int i=0; i < 3; i++) {
          if( graph->vertices[graph->id[current]]->data[i] != NULL)
            printf("%s\n", graph->vertices[graph->id[current]]->data[i]);
        }
        while( getchar() != '\n' );
        break;
      default :
        if( ch == CTRL_KEY('e') ){
          destroyLinks(&head);
          return ch;
        }
        break;
    }
  }while( ch != EXIT );
  destroyLinks(&head);
  return '0';
}
/*
* 函数介绍: 读取csv文件格式文件到图结构
* 参数:
* 返回值: 成功:返回0;失败,返回EXIT_FAILURE。
* 备注: 使用fgetc对文件读写时,文件越大读取速度越慢。需改进
*/
int readALG(ALGraph *graph, char *filename, int flag)
{
  links *head = initLinks();
  if( head == NULL ){
    writeLog("%s, %s, %d:内存错误\n", __FILE__, __func__, __LINE__);
    return EXIT_FAILURE;
  }
  FILE *fp = fopen(filename, "r");
  if( fp == NULL ){
    writeLog("%s, %s, %d:%s文件丢失\n", __FILE__, __func__, __LINE__, filename);
    return EXIT_FAILURE;
  }
  char *str = NULL, *topStr = NULL;
  bool endOfField = true, endOfLine = false, quotationMarks = false, formatError = false, memoryError = false;
  dualNode *temp = NULL, *del = NULL;
  int fieldCount = 0, topNum = 0, strLength = 1, c = 0;
  while( (c = fgetc(fp)) != EOF ){
    if( isblank(c) != 0 ){
      continue; //跳过字段开始的空白字符
    }
    endOfField = true;  //读取了一个字段
    endOfLine = false;  //读取了一行
    quotationMarks = false; //字段中有特殊符号
    memoryError = false;  //内存分配失败
    head->top = head->bottom; //初始化链表
    head->bottom->element = 0;
    do {
      if( pushStart(head, c) == 1 ){
        memoryError = true;
        break;
      }
      if( c == '"' ){
        endOfField = !endOfField;
        quotationMarks = true;
      }
      if( endOfField && (c == ',' || c == '\n' || c == '\r') ){
        if( c == '\n' || c == '\r' ){
          endOfLine = true;
        }
        popStart(head);
        break;
      }
      c = fgetc(fp);
    }while(c != EOF);
    if( memoryError ){
      break;
    } else if( ferror(fp) ){
      writeLog("%s, %s, %d:%s读取时错误\n", __FILE__, __func__, __LINE__, filename);
      break;
    } else if( feof(fp) ){
      writeLog("完整读取文件 %s\n", filename);
      break;
    } else if( !endOfField ){
      writeLog("%s格式错误\n", filename);
      break;
    }
    while( isblank(head->top->element) != 0 ){
      popStart(head); //删除字段结尾的空白字符
    }
    if( quotationMarks == true ){ //特殊字符串格式检查
      if( head->top->element != '"' || head->bottom->pripor->element != '"' )
        continue;
      popStart(head);
      popEnd(head);
      temp = head->top;
      del = NULL;
      while( temp != head->bottom ){
        if( temp->element == '"' ){
          del = temp->next;
          if( del->element == '"' ){
            temp->next = del->next;
            del->next->pripor = temp;
            head->top->pripor->next = del;
            del->pripor = head->top->pripor;
            head->top->pripor = del;
            del->next = head->top;
            head->bottom->element--;
          } else {
            formatError = true;
            break;
          }
        }
        temp = temp->next;
      }
      if( formatError ){
        formatError = false;
        continue;
      }
    }
    strLength = lengthLinks(head);
    str = NULL;
    str = (char *)malloc((strLength+1) * sizeof(char));
    if( str == NULL ){
      writeLog("%s, %s, %d:内存错误\n", __FILE__, __func__, __LINE__);
      break;
    }
    for( int i=0; i < strLength; i++ ){
      str[i] = popEnd(head);
    }
    str[strLength] = '\0';
    fieldCount++;
    if( graph->vexnum >= MAX_VERTEX_NUM && graph->arcnum >= MAX_ARC_NUM ){
      printf("已达到存储极限\n");
      break;
    }
    if( flag == 0 ){
      if( fieldCount == 1 ){
        topNum = binarySearch(graph, str);
        if( topNum == -1 && *str != '\0' ){
          topNum = insertVex(graph, str);
        } else {
          free(str);
        }
      } else{
        if( topNum != -1 ){
          if( fieldCount <= 3 && *str != '\0' ){
              free(graph->vertices[graph->id[topNum]]->data[fieldCount-1]);
              graph->vertices[graph->id[topNum]]->data[fieldCount-1] = str;
          } else {
            free(str);
          } 
        }
      }
    } else if( flag == 1 ){
      topNum = binarySearch(graph, str);
      if( topNum == -1 && *str != '\0' ){
        topNum = insertVex(graph, str);
      } else {
        free(str);
      }
      if( fieldCount == 1 ){
        if( topNum != -1 ){
          topStr = graph->vertices[graph->id[topNum]]->data[0];
        } else {
          topStr = NULL;
        }
      } else {
        if( topStr != NULL ){
          insertArc(graph, binarySearch(graph, topStr), topNum);
        }
      }
    }
    if( endOfLine ){
      fieldCount = 0;
    }
  }
  if( ferror(fp) ){
    writeLog("%s, %s, %d:%s读取时错误\n", __FILE__, __func__, __LINE__, filename);
  } else if( feof(fp) ){
    writeLog("读取节点%d,连接%d\n", graph->vexnum, graph->arcnum );
  }
  fclose(fp);
  destroyLinks(&head);
  return 0;
}
/*
* 函数介绍: 保存子符串为csv格式
* 参数:
* 返回值:
* 备注:
*/
void saveCSV(char *str, FILE *fp)
{
  unsigned int flag = false;
  if( str == NULL )
    return;
  char *tmp = str;
  if( isblank(*tmp) != 0 )
    flag = true;
  while( *tmp != '\0' ){
    if( *tmp == ',' || *tmp == '\n' || *tmp == '"' ){
      flag = true;
      break;
    }
    tmp++;
  }
  tmp--;
  if( isblank(*tmp) != 0 )
    flag = true;
  tmp = str;
  if( flag ){
    putc('\"', fp);
    while( *tmp != '\0' ){
      if( *tmp == '"' )
        putc('\"', fp);
      putc(*tmp, fp);
      tmp++;
    }
    putc('\"', fp);
  } else {
    while( *tmp != '\0' ){
      putc(*tmp, fp);
      tmp++;
    }
  }
}
/*
* 函数介绍: 保存“图”中数据到文件中
* 参数:
* 返回值:
* 备注:
*/
int saveALG(ALGraph *graph, char *filename, int flag)
{
  int newline = '\n';
  if( graph->vexnum > 0 ){
    FILE *fp = fopen(filename, "wb");
    if( fp == NULL ){
      return EXIT_FAILURE;
    }
    if( flag == 0 ){
      for( int i=0; i < graph->vexnum; i++ ){
        if( graph->vertices[graph->id[i]]->data[1] == NULL
            && graph->vertices[graph->id[i]]->data[2] == NULL ){
          continue;
        } else {
          saveCSV(graph->vertices[graph->id[i]]->data[0], fp);
          for( int j=1; j < 3; j++ ){
            putc(',', fp);
            saveCSV(graph->vertices[graph->id[i]]->data[j], fp);
          }
          putc(newline, fp);
        }
      }
    } else if( flag == 1 ){
      dualNode *tmp = NULL;
      for( int i=0; i < graph->vexnum; i++ ){
        saveCSV(graph->vertices[graph->id[i]]->data[0], fp);
        tmp = graph->vertices[graph->id[i]]->nextArc->bottom;
        for( int i=tmp->element; i > 0; i-- ){
          tmp = tmp->pripor;
          putc(',', fp);
          saveCSV(graph->vertices[tmp->element]->data[0], fp);
        }
        putc(newline, fp);
      }
    }
    fclose(fp);
  }
  return true;
}
/*
* 函数介绍: 计算KMP的NEXT数组
* 参数:
* 返回值:
* 备注:
*/
void Next(char *T, int *next)
{
  unsigned int i = 1;
  next[1] = 0;
  unsigned int j = 0;
  while( i < strlen(T) ){
    if( j == 0 || T[i-1] == T[j-1] ){
      i++;
      j++;
      if( T[i-1] != T[j-1] ){
        next[i] = j;
      } else {
        next[i] = next[j];
      }
    } else {
      j = next[j];
    }
  }
}
/*
* 函数介绍: KMP快速匹配子串,顺序查找
* 参数:
* 返回值:
* 备注: MAX_VERTEX_NUM为没找到匹配的子串
*/
int KMP(ALGraph *graph, char *T)
{
  int next[max_num] = {0}, pattern[max_num] = {0};
  Next(T, next);
  int i = 1, j = 1;
  char *S = NULL;
  int lenS = 0, lenT = strlen(T);
  int num = 0, resoult = MAX_VERTEX_NUM;
  for(num = 0; num < graph->vexnum; num++) {
    S = graph->vertices[graph->id[num]]->data[0];
    lenS = strlen(S);
    if( lenS < lenT ) continue;
    i = 1;
    j = 1;
    memcpy(pattern, next, max_num);
    while( i <= lenS && j <= lenT ){
      if( j == 0 || S[i-1] == T[j-1] ){
        i++;
        j++;
      } else {
        j = pattern[j];
      }
    }
    if( j > lenT ){
      resoult = num;
      printf("%s,按'5'结束搜索:", S);
      if( getInt() == '5' )
        return resoult;
    }
  }
  return resoult;
}
/*
* 函数介绍:UTF-8解码器,将UTF8转换为 UNICODE,参考www.cnblogs.com/mickole/articles/3663924.html
* 参数:utf8指向以UTF8编码的字符,unicode指向保存的数据即unicode编码
* 返回值:成功返回utf8编码所占用的字节数;失败返回0.
* 备注:utf8没有字节序问题,但是unicode有字节序要求;
* 字节序分为大端(Big Endian)和小端(Little Endian)两种;
* 在Intel处理器中采用小端法表示,在此采用小端法表示。(低地址存低位)
*/
int utf8ToUnicode(char *utf8, unsigned long *unicode)
{
  *unicode = 0;
  unsigned char *pOutput = (unsigned char *)unicode;
  unsigned char temp = 0x80;
  int utfBytes = 0;
  while( temp & *utf8 ){
    utfBytes++;
    temp = temp>>1;
    if( utfBytes > 1 && ((*(utf8 + utfBytes - 1) & 0xC0) != 0x80) )
      return 0; //除了首字节,后面的字节前导均为“10”
  }
  switch( utfBytes ){
  case 0:
    *pOutput = *utf8;
    utfBytes++;
    break;
  case 2:
    *pOutput = (*utf8 << 6) + (*(utf8+1) & 0x3F);
    *(pOutput+1) = (*utf8 >> 2) & 0x07;
    break;
  case 3:
    *pOutput = (*(utf8+1) << 6) + (*(utf8+2) & 0x3F);
    *(pOutput +1) = (*utf8 << 4) + ((*(utf8+1) >> 2) & 0x0F);
    break;
  case 4:
    *pOutput = (*(utf8+2) << 6) + (*(utf8+3) & 0x3F);
    *(pOutput+1) = (*(utf8+1) << 4) + ((*(utf8+2) >> 2) & 0x0F);
    *(pOutput+2) = ((*utf8 << 2) & 0x1C) + ((*(utf8+1) >> 4) & 0x03);
    break;
  case 5:
    *pOutput = (*(utf8+3) << 6) + (*(utf8+4) & 0x3F);
    *(pOutput+1) = (*(utf8+2) << 4) + ((*(utf8+3) >> 2) & 0x0F);
    *(pOutput+2) = (*(utf8+1) << 2) + ((*(utf8+2) >> 4) & 0x03);
    *(pOutput+3) = (*utf8 << 6);
    break;
  case 6:
    *pOutput = (*(utf8+4) << 6) + (*(utf8+5) & 0x3F);
    *(pOutput+1) = (*(utf8+4) << 4) + ((*(utf8+5) >> 2) & 0x0F);
    *(pOutput+2) = (*(utf8+2) << 2) + ((*(utf8+3) >> 4) & 0x03);
    *(pOutput+3) = ((*utf8 << 6) & 0x40) + (*(utf8+1) & 0x3F);
    break;
  default:
    return 0;
    break;
  }
  return utfBytes;
}
/*
* 函数介绍:写入日志文件
* 参数:
* 返回值:
* 备注:
*/
int writeLog(const char *format, ...)
{
  FILE *pFile = fopen("logs.log", "a+"); 
  if( pFile == NULL ){
    return -1;
  }
  va_list arg;
  int done = 0;
  va_start(arg, format);
  time_t time_log = time(NULL);
  struct tm *tm_log = localtime(&time_log);
  fprintf(pFile, "%04d-%02d-%02d %02d:%02d:%02d ", tm_log->tm_year + 1900, tm_log->tm_mon + 1, tm_log->tm_mday, tm_log->tm_hour, tm_log->tm_min, tm_log->tm_sec);  
  done = vfprintf(pFile, format, arg);
  va_end(arg);
  fflush(pFile);
  fclose(pFile);
  return done;
}
程序代码
 1 # INI文件由节、键、值组成
 2 # 节 
 3 # [section]
 4 # 参数(键=值)
 5 # name = value
 6 # 注解使用 # 号,开头为 # 号的行为注释
 7 [file]
 8 inputArc.csv=边信息.csv
 9 inputVex.csv=节点信息.csv
10 addArc.csv=新增边.csv
11 addVex.csv=新增节点.csv
12 outputArc.csv=输出边.csv
13 outputVex.csv=输出节点.csv
posted @ 2021-06-07 13:05  偊偊而步  阅读(163)  评论(0编辑  收藏  举报