INI库:链表(摘要)问题

时间:2011-05-15 22:37:05

标签: 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
{
 char *section;
 struct keys *okeys;
 unsigned long hashkey;
 struct ini *next;
 struct ini *prev;
}

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

这是我简单的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);
}

1 个答案:

答案 0 :(得分:0)

我会将这些部分保留在链表中。有可能平均ini文件中没有足够的部分来保证设置哈希表。或者,您可以使用需要最少工作量的二叉搜索树。

另一种方法是使用一个哈希表,并使用节名称和分隔标记为每个值添加前缀。此方法的缺点是您不能在密钥名称​​和部分名称中使用分隔标记。如果你控制ini格式,那么这没问题。如果这是一般的ini解析器,它将是。

对于这两种方法,您需要为无节段即全局部分提供节名称。

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