C INI库:链表(摘要)问题

C INI库:链表(摘要)问题,c,parsing,linked-list,ini,C,Parsing,Linked List,Ini,我正在开发自己的简单、简短、愚蠢的ini库,我将用于其他项目。我编写了一个ini解析器,非常简单,到目前为止非常有效,现在是时候进入更难的部分了——如何以及在何处存储ini文件中解析的数据 我曾想过使用简单的链表(可能是双链表),但我无法在脑海中想象它 struct keys { char *keyname; char *keyval; unsigned long hashkey; struct keys *next; struct keys *prev; } struct ini

我正在开发自己的简单、简短、愚蠢的ini库,我将用于其他项目。我编写了一个ini解析器,非常简单,到目前为止非常有效,现在是时候进入更难的部分了——如何以及在何处存储ini文件中解析的数据

我曾想过使用简单的链表(可能是双链表),但我无法在脑海中想象它

struct keys
{
 char *keyname;
 char *keyval;
 unsigned long hashkey;
 struct keys *next;
 struct keys *prev;
}

struct ini
{
 char *section;
 struct keys *okeys;
 unsigned long hashkey;
 struct ini *next;
 struct ini *prev;
}

首先,我想用这种方式编码,但我看起来有点愚蠢,不够有效,我知道有更好的方法在链表中存储节、键和键值,但我想不出任何方法。或者,也许我应该尝试使用其他数据结构?我不知道,这样做ini库需要太多的函数,比如一个用于添加节的单独函数,一个用于添加键的函数,等等。我真的很想弄清楚该使用什么,我被卡住了

下面是我的简单ini解析器的代码:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

#define BUFF_SIZE 1024
#define INI_FILE "config.ini"
#define ERROR_FILE "error.log"

void load_ini(const char *name);
void log_it(const char *filename, const char *format, ...);
void trim(char *str);
unsigned int hash(const char *key);

int main(int argc, char *argv[])
{
 load_ini(INI_FILE);
 return 0;
}

/*2. load ini function */
void load_ini(const char *name)
{
 char *line = NULL;
 char *p = NULL;
 FILE *ifile = NULL;

 ifile = fopen(name, "r");
 if(ifile == NULL)
 {
  log_it(ERROR_FILE, "(load_ini) can't open %s file", name);
  return;
 }

 line = malloc(BUFF_SIZE * sizeof(char) + 1);
 if(line == NULL)
 {
  log_it(ERROR_FILE, "(load_ini) malloc fail");
  return;
 }

 while(fgets(line, BUFF_SIZE+1, ifile) != NULL)
 {
  trim(line);

  if(strlen(line) < 3 || *line == ';' || *line == '#')
  {
   continue;
  }

  if(*line == '[')
  {
   if(p = strchr(line, ']'))
   {
    *p = '\0';
    trim(line+1);
    /* add section here */
   }
  }
  else if(p = strchr(line, '='))
  {
   *p = '\0';
   trim(line);
   trim(p+1);
   /* add key, and key value here */
  }
 }
 free(line);
 fclose(ifile);
}

/* 3. multifunctional log function with variable number of arguments */
void log_it(const char *filename, const char *format, ...)
{
 va_list arglist;
 FILE *fp = NULL; 

 fp = fopen(filename,"a");
 if(fp == NULL)
 {
  /* :) */
  return;
 }

 va_start(arglist, format);
 vfprintf (fp, format, arglist);
 va_end (arglist);
 fclose (fp);
}

/* 4. trim function */

void trim(char *str)
{
 char *start = str;
 char *end = start + strlen(str);

 while (--end >= start) 
 { 
  if (!isspace(*end))
  {
   break;
  }
 }

 *(++end) = '\0';

 while (isspace(*start)) 
 {
  start++;
 }

 if (start != str)
 {
  memmove(str, start, end - start + 1);
 }
}

/* Bernstein hash */
unsigned int hash(const char *key) 
{
 unsigned int hash = 5381;
 unsigned int i = 0;
 int len = strlen(key);

 for(i; i < len; ++i)
 {
  hash = 33 * hash + key[i];
 }
 return hash ^ (hash >> 16);
}
#包括
#包括
#包括
#包括
#定义BUFF_大小1024
#定义INI_文件“config.INI”
#定义错误文件“ERROR.log”
无效加载ini(常量字符*名称);
作废日志(常量字符*文件名,常量字符*格式,…);
空隙修剪(字符*str);
无符号整数散列(常量字符*键);
int main(int argc,char*argv[])
{
加载ini(ini文件);
返回0;
}
/*2. 加载ini函数*/
无效加载ini(常量字符*名称)
{
char*line=NULL;
char*p=NULL;
文件*ifile=NULL;
ifile=fopen(名称,“r”);
如果(ifile==NULL)
{
日志(错误文件,“(加载ini)无法打开%s文件”,名称);
返回;
}
line=malloc(BUFF_SIZE*sizeof(char)+1);
如果(行==NULL)
{
记录(错误文件,“(加载ini)malloc失败”);
返回;
}
while(fgets(线条,BUFF_SIZE+1,ifile)!=NULL)
{
修剪(线);
如果(strlen(line)<3 | |*line=''.'| | |*line==''.'.'|')
{
继续;
}
如果(*行=='[')
{
如果(p=strchr(行“]'))
{
*p='\0';
修剪(线条+1);
/*在此处添加节*/
}
}
else if(p=strchr(行“=”))
{
*p='\0';
修剪(线);
修剪(p+1);
/*在此处添加键和键值*/
}
}
自由线;
fclose(ifile);
}
/* 3. 具有可变参数数的多功能日志函数*/
作废日志(常量字符*文件名,常量字符*格式,…)
{
va_列表arglist;
FILE*fp=NULL;
fp=fopen(文件名,“a”);
如果(fp==NULL)
{
/* :) */
返回;
}
va_开始(arglist,格式);
vfprintf(fp、格式、arglist);
va_end(arglist);
fclose(fp);
}
/* 4. 修剪功能*/
空心修剪(字符*str)
{
char*start=str;
char*end=start+strlen(str);
而(--end>=start)
{ 
如果(!isspace(*end))
{
打破
}
}
*(++end)='\0';
while(isspace(*start))
{
启动++;
}
如果(开始!=str)
{
memmove(str,start,end-start+1);
}
}
/*伯恩斯坦哈希*/
无符号整数散列(常量字符*键)
{
无符号整数散列=5381;
无符号整数i=0;
int len=strlen(键);
对于(i;i>16);
}

我会将这些章节保存在一个链接列表中。很可能在平均ini文件中没有足够的节来保证设置哈希表。或者,您可以使用二叉搜索树,只需最少的努力

另一种方法是使用一个哈希表,并在每个值前面加上节名和分隔标记。这种方法的缺点是不能在键名称和节名称中使用分离标记。如果您控制ini格式,那么这就没有问题。如果这是一个通用的ini解析器,它将是

对于任何一种方法,您都需要为无节(即全局节)提供节名称


虽然二进制搜索树和哈希表是快速查找,然后链接列表,但您需要考虑它是否值得应用程序。如果应用程序只读取ini文件一次,并且应用程序中的值集状态在其生命周期内不再查询,则查找速度并不重要。但是,如果应用程序不断查询这些值,则首选哈希表。如果在应用程序生命周期内更有可能查询某些值,则splay树可能会工作良好。如果访问配置值是应用程序中重要且可测量的一部分,那么也许是时候分离一些功能了。

哈希表在我看来是个好主意:一个用于部分,每个部分包含/指向另一个用于键/值对的哈希表。首先,感谢您的帮助。我曾想过使用二叉树,但正如您所说,当应用程序频繁地读/写ini文件时,通常需要使用二叉树,而我的情况并非如此。我的程序(irc机器人)将在内存中加载一次ini文件,并在需要时使用数据。所以,正如你所建议的,链表是实现这一点的方法。