C語言實現<讀取>和<寫入> *.ini文件(轉)


原地址:https://blog.csdn.net/niha1993825jian/article/details/41086403

#include <stdio.h>  
#include <errno.h>  
#include <ctype.h>  
#include <string.h>  
#include <stdlib.h>  
#include <stdbool.h>  
  
#define  MAX_VALUE  64 /* 定義section,key,value字符串最大長度 */  
// printf("File = %s\nLine = %d\nFunc=%s\nDate=%s\nTime=%s\n", __FILE__, __LINE__, __FUNCTION__, __DATE__, __TIME__);  
#define  PRINT_ERRMSG(STR) fprintf(stderr,"line:%d,msg:%s,eMsg:%s\n", __LINE__, STR, strerror(errno))  
  
typedef struct _option {  
  char    key[MAX_VALUE];   /* 對應鍵 */  
  char    value[MAX_VALUE]; /* 對應值 */  
  struct  _option *next;    /* 鏈表連接標識 */  
}Option;  
  
typedef struct _data {  
  char    section[MAX_VALUE]; /* 保存section值          */  
  Option  *option;            /* option鏈表頭           */  
  struct  _data *next;        /* 鏈表連接標識           */  
}Data;  
  
typedef struct {  
  char    comment;              /* 表示注釋的符號    */  
  char    separator;            /* 表示分隔符        */  
  char    re_string[MAX_VALUE]; /* 返回值字符串的值  */  
  int     re_int;               /* 返回int的值       */  
  bool    re_bool;              /* 返回bool的值      */  
  double  re_double ;           /* 返回double類型    */  
  Data    *data;                /* 保存數據的頭      */  
}Config;  
  
/** 
* 判斷字符串是否為空 
* 為空返回true,不為空返回false 
**/  
bool str_empty(const char *string)  
{  
  return NULL == string || 0 == strlen(string);  
}  
  
/** 
* 向鏈表添加section,key,value 
* 如果添加時不存在section則新增一個 
* 如果對應section的key不存在則新增一個 
* 如果section已存在則不會重復創建 
* 如果對應section的key已存在則只會覆蓋key的值 
**/  
bool cnf_add_option(Config *cnf, const char *section, const char *key, const char *value)  
{  
  if (NULL == cnf || str_empty(section) || str_empty(key) || str_empty(value)) {  
    return false; /* 參數不正確,返回false */  
  }  
    
  Data *p = cnf->data; /* 讓變量p循環遍歷data,找到對應section */  
  while (NULL != p && 0 != strcmp(p->section, section)) {  
    p = p->next;  
  }  
    
  if (NULL == p) { /* 說明沒有找到section,需要加一個 */  
    Data *ps = (Data*)malloc(sizeof(Data));  
    if (NULL == ps) {  
      exit(-1); /* 申請內存錯誤 */  
    }  
    strcpy(ps->section, section);  
    ps->option = NULL;    /* 初始的option要為空 */  
    ps->next = cnf->data; /* cnf->data可能為NULL */  
    cnf->data = p = ps;   /* 頭插法插入鏈表 */  
  }  
    
  Option *q = p->option;  
  while (NULL != q && 0 != strcmp(q->key, key)) {  
    q = q->next; /* 遍歷option,檢查key是否已經存在 */  
  }  
    
  if (NULL == q) { /* 不存在option,則新建一個 */  
    q = (Option*)malloc(sizeof(Option));  
    if (NULL == q) {  
      exit(-1); /* 申請內存錯誤 */  
    }  
    strcpy(q->key, key);  
    q->next = p->option; /*這里p->option可能為NULL,不過也沒關系 */  
    p->option = q; /* 頭插法插入鏈表 */  
  }  
  strcpy(q->value, value); /* 無論如何要把值改了 */  
    
  return true;  
}  
  
/** 
* 去掉字符串內所有空白 
* 且忽略注釋部分 
* 最終得到沒有空白的字符串 
**/  
bool strip_comments(char *string, char comment)  
{  
  if (NULL == string || '\n' == *string || '\r' == *string) {  
    return false; /* 第一個字符為回車或換行,表示空行 */  
  }  
    
  char *p, *q; /* 下面去掉字符串中所有空白字符 */  
  for (p = q = string; *p != '\0' && *p != comment; p++) {  
    if (0 == isspace(*p)) {  
      *q++ = *p; /* 不是空白字符則重寫字符串 */  
    }  
  }  
  *q = '\0';  
    
  return 0 != strlen(string); /* 字符串長度不為0,表示數據可用 */  
}  
  
/** 
* 傳遞配置文件路徑 
* 參數有文件路徑,注釋字符,分隔符 
* 返回Config結構體 
**/  
Config *cnf_read_config(const char *filename, char comment, char separator)  
{  
  Config *cnf     = (Config*)malloc(sizeof(Config));  
  cnf->comment    = comment; /* 每一行該字符及以后的字符將丟棄 */  
  cnf->separator  = separator; /* 用來分隔Section 和 數據 */  
  cnf->data       = NULL; /* 初始數據為空 */  
    
  if (str_empty(filename)) {  
    return cnf; /* 空字符串則直接返回對象 */  
  }  
    
  char *p, sLine[MAX_VALUE];    /* 保存一行數據到字符串 */  
  char section[MAX_VALUE], key[MAX_VALUE], value[MAX_VALUE]; /* 緩存section,key,value */  
  FILE *fp = fopen(filename, "r");  
  if(NULL == fp) {  
    PRINT_ERRMSG("fopen");  
    exit(errno); /* 讀文件錯誤直接按照錯誤碼退出 */  
  }  
    
  while (NULL != fgets(sLine, MAX_VALUE, fp)) {  
    if (strip_comments(sLine, cnf->comment)) { /* 去掉字符串所有空白,注釋也忽略 */  
      if ('[' == sLine[0] && ']' == sLine[strlen(sLine)-1]) {  
        memset(section, '\0', MAX_VALUE); /* 清空section,因為strncpy不追加'\0' */  
        strncpy(section, sLine+1, strlen(sLine)-2);  
      } else if (NULL != (p = strchr(sLine, cnf->separator))) {  /* 存在分隔符 */  
        memset(key,   '\0', MAX_VALUE); /* 清空key,因為strncpy不追加'\0' */  
        strncpy(key,  sLine, p - sLine);  
        strcpy(value, p + 1); /* strcpy會追加'\0',所以妥妥噠 */  
        cnf_add_option(cnf, section, key, value); /* 添加section,key,value */  
      } /* 如果該行不存在分隔符則忽略這一行 */  
    } /* end strip_comments */  
  } /* end while */  
    
  fclose(fp);  
  return cnf;  
}  
  
/** 
* 獲取指定類型的值 
* 根據不同類型會賦值給對應值 
* 本方法需要注意,int和double的轉換,不滿足就是0 
*     需要自己寫代碼時判斷好 
**/  
bool cnf_get_value(Config *cnf, const char *section, const char *key)  
{  
  Data *p = cnf->data; /* 讓變量p循環遍歷data,找到對應section */  
  while (NULL != p && 0 != strcmp(p->section, section)) {  
    p = p->next;  
  }  
    
  if (NULL == p) {  
    PRINT_ERRMSG("section not find!");  
    return false;  
  }  
    
  Option *q = p->option;  
  while (NULL != q && 0 != strcmp(q->key, key)) {  
    q = q->next; /* 遍歷option,檢查key是否已經存在 */  
  }  
    
  if (NULL == q) {  
    PRINT_ERRMSG("key not find!");  
    return false;  
  }  
    
  strcpy(cnf->re_string, q->value);       /* 將結果字符串賦值 */  
  cnf->re_int    = atoi(cnf->re_string);  /* 轉換為整形 */  
  cnf->re_bool   = 0 == strcmp ("true", cnf->re_string); /* 轉換為bool型 */  
  cnf->re_double = atof(cnf->re_string);  /* 轉換為double型 */  
    
  return true;  
}  
  
/** 
* 判斷section是否存在 
* 不存在返回空指針 
* 存在則返回包含那個section的Data指針 
**/  
Data *cnf_has_section(Config *cnf, const char *section)  
{  
  Data *p = cnf->data; /* 讓變量p循環遍歷data,找到對應section */  
  while (NULL != p && 0 != strcmp(p->section, section)) {  
    p = p->next;  
  }  
    
  if (NULL == p) { /* 沒找到則不存在 */  
    return NULL;  
  }  
    
  return p;  
}  
  
/** 
* 判斷指定option是否存在 
* 不存在返回空指針 
* 存在則返回包含那個section下key的Option指針 
**/  
Option *cnf_has_option(Config *cnf, const char *section, const char *key)  
{  
  Data *p = cnf_has_section(cnf, section);  
  if (NULL == p) { /* 沒找到則不存在 */  
    return NULL;  
  }  
    
  Option *q = p->option;  
  while (NULL != q && 0 != strcmp(q->key, key)) {  
    q = q->next; /* 遍歷option,檢查key是否已經存在 */  
  }  
  if (NULL == q) { /* 沒找到則不存在 */  
    return NULL;  
  }  
    
  return q;  
}  
  
/** 
* 將Config對象寫入指定文件中 
* header表示在文件開頭加一句注釋 
* 寫入成功則返回true 
**/  
bool cnf_write_file(Config *cnf, const char *filename, const char *header)  
{  
  FILE *fp = fopen(filename, "w");  
  if(NULL == fp) {  
    PRINT_ERRMSG("fopen");  
    exit(errno); /* 讀文件錯誤直接按照錯誤碼退出 */  
  }  
    
  if (0 < strlen(header)) { /* 文件注釋不為空,則寫注釋到文件 */  
    fprintf(fp, "%c %s\n\n", cnf->comment, header);  
  }  
    
  Option *q;  
  Data   *p = cnf->data;  
  while (NULL != p) {  
    fprintf(fp, "[%s]\n", p->section);  
    q = p->option;  
    while (NULL != q) {  
      fprintf(fp, "%s %c %s\n", q->key, cnf->separator, q->value);  
      q = q->next;  
    }  
    p = p->next;  
  }  
    
  fclose(fp);  
  return true;  
}  
  
/** 
* 刪除option 
**/  
bool cnf_remove_option(Config *cnf, const char *section, const char *key)  
{  
  Data *ps = cnf_has_section(cnf, section);  
  if (NULL == ps) { /* 沒找到則不存在 */  
    return NULL;  
  }  
    
  Option *p, *q;  
  q = p = ps->option;  
  while (NULL != p && 0 != strcmp(p->key, key)) {  
    if (p != q) { q = q->next; } /* 始終讓q處於p的上一個節點 */  
    p = p->next;  
  }  
    
  if (NULL == p) { /* 沒找到則不存在 */  
    return NULL;  
  }  
    
  if (p == q) { /* 第一個option就匹配了 */  
    ps->option = p->next;  
  } else {  
    q->next = p->next;  
  }  
    
  free(p);  
  q = p = NULL; // 避免野指針  
    
  return true;  
}  
  
/** 
* 刪除section 
**/  
bool cnf_remove_section(Config *cnf, const char *section)  
{  
  if (str_empty(section)) {  
    return false;  
  }  
    
  Data *p, *q;  
  q = p = cnf->data; /* 讓變量p循環遍歷data,找到對應section */  
  while (NULL != p && 0 != strcmp(p->section, section)) {  
    if (p != q) { q = q->next; } /* 始終讓q處於p的上一個節點 */  
    p = p->next;  
  }  
    
  if (NULL == p) { /* 沒有找到section */  
    return false;  
  }  
    
  if (p == q) { /* 這里表示第一個section,因此鏈表頭位置改變 */  
    cnf->data = p->next;  
  } else { /* 此時是中間或尾部節點 */  
    q->next = p->next;  
  }  
    
  Option *o = p->option;  
  while (NULL != o) {  
    free(o); /* 循環釋放所有option */  
    o = o->next;  
  }  
  p->option = NULL; // 避免野指針  
  free(p); /* 釋放刪除的section */  
  q = p = NULL;  // 避免野指針  
    
  return true;  
}  
  
/** 
* 打印當前Config對象 
**/  
void print_config(Config *cnf)  
{  
  Data *p = cnf->data; // 循環打印結果  
  while (NULL != p) {  
    printf("[%s]\n",p->section);  
      
    Option *q = p->option;  
    while (NULL != q) {  
      printf("  %s %c %s\n", q->key, cnf->separator, q->value);  
      q = q->next;  
    }  
    p = p->next;  
  }  
}  
  
/** 
* 主程序,放在最底下 
* 避免重復聲明其他函數 
**/  
int main(int argc, char *argv[])  
{  
  // 讀取配置文件cnf.ini,注釋字符為#,分隔鍵值字符為=  
  Config *cnf = cnf_read_config("cnf.ini", '#', '=');  
  if (NULL == cnf) {  
    return -1; /* 創建對象失敗 */  
  }  
    
  printf("-------------- After Read File --------------\n");  
  print_config(cnf); // 打印cnf對象    
  cnf_remove_section(cnf,"AAA"); // 刪除AAA的section  
  cnf_remove_option(cnf, "CC","df");  // 刪除CC下的df  
  printf("-------------- After remove --------------\n");  
  print_config(cnf); // 打印cnf對象    
  cnf_add_option(cnf, "NEW", "new_1", "true"); // 新增NEW下的new_1的值  
  cnf_add_option(cnf, "NEW", "new_2", "asdas"); // 新增NEW下的new_2的值  
  cnf_add_option(cnf, "NEW1", "new_2", "true");  
  printf("-------------- After add --------------\n");  
  print_config(cnf); // 打印cnf對象    
    
  cnf_get_value(cnf, "NEW1", "new_2"); // 獲取NEW1下的new_2值  
  printf("cnf_get_value:%s,%d,%d,%f\n",cnf->re_string,cnf->re_int,cnf->re_bool,cnf->re_double);  
    
  cnf->separator = ':'; // 將分隔符改成 : ,冒號  
  cnf_write_file(cnf, "cnf_new.ini", "write a new ini file!"); // 將對象寫入cnf_new.ini文件  
  return 0;  
}  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM