首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >解析ini文件

解析ini文件
EN

Code Review用户
提问于 2015-10-21 11:40:31
回答 2查看 1K关注 0票数 3

我已经用C编写了一个基本的ini文件解析器。它不支持大ini文件,在获取配置值时也不是很快,但是对于小文件来说应该足够了。

我找不到ini文件标准的“适当”定义,因此没有处理筛选符号,解析过程中遇到的所有和任何错误都被默默地吞没了。

inifile.h

代码语言:javascript
运行
复制
/** \file inifile.h
 * Functions for reading configuration from disk
 * Configuration options have single lines
 * Comments start with ';'
 */

#ifndef _INIFILE_H_
#define _INIFILE_H_

typedef void* pinifile_data;

/** Open and parse an ini file
 * \param[in] fname name of file to read
 * \param[out] pinidb pointer to the variable for storing reference for
 * inifile data
 * \returns 0 on success
 */
int open_ini_file(const char* fname, pinifile_data* pinidb);

int get_integer(const pinifile_data inidb, const char* section, const char* entry, int default_value);
double get_real(const pinifile_data inidb, const char* section, const char* entry, double default_value);

/** Free memory used by ini file database
 * \param[in] inidb ini file database from open_ini_file
 */
void close_ini_file(pinifile_data inidb);

/** Create ini file database */
pinifile_data create_inidb();

/** Add or change value in ini file database
 * If there is no specified section or entry in database, they will be created
 * \param inidb ini file database
 * \param[in] section name of a section
 * \param[in] entry name of an entry
 * \param[in] value new value of entry in string form
 */
void set_entry(pinifile_data inidb, const char* section, const char* entry, const char* value);

/** Dump ini file database on disc
 * \param fname name of a file to dump into
 * \param inidb ini file database
 * \returns 0 on success
 */
int dump_ini_file(const char* fname, pinifile_data inidb);

#endif // _INIFILE_H_

inifile.c

代码语言:javascript
运行
复制
#include "inifile.h"

#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <ctype.h>

struct inifile_entry
{
    const char* entry_name;
    const char* entry_value;
    struct inifile_entry* next_entry;
};

struct inifile_section
{
    const char* section_name;
    struct inifile_entry* first_entry;
    struct inifile_section* next_section;
};

struct inifile_data
{
    struct inifile_section* first_section;
};

void close_ini_file(pinifile_data inidb)
{
    struct inifile_data* pinidb = (struct inifile_data*)inidb;
    if(pinidb == NULL)
        return;

    struct inifile_section* pnext_s = NULL, * pcur_s = NULL;
    struct inifile_entry* pnext_e = NULL, * pcur_e = NULL;

    pcur_s = pinidb->first_section;
    while(pcur_s != NULL)
    {
        pnext_s = pcur_s->next_section;

        pcur_e = pcur_s->first_entry;
        while(pcur_e != NULL)
        {
            pnext_e = pcur_e->next_entry;
            if(pcur_e->entry_name != NULL)
                free((void*)pcur_e->entry_name);
            if(pcur_e->entry_value != NULL)
                free((void*)pcur_e->entry_value);
            free((void*)pcur_e);
            pcur_e = pnext_e;
        }
        if(pcur_s->section_name != NULL)
            free((void*)pcur_s->section_name); 
        free((void*)pcur_s);
        pcur_s = pnext_s;
    }
    free((void*)pinidb);
}

void init_section(struct inifile_section* psection)
{
    psection->section_name = NULL;
    psection->first_entry = NULL;
    psection->next_section = NULL;
}

void init_entry(struct inifile_entry* pentry)
{
    pentry->entry_name = NULL;
    pentry->entry_value = NULL;
    pentry->next_entry = NULL;
}

enum parser_states
{
    comment = 0,
    linestart,
    sectionentry,
    entryname,
    entryvalue,
    states_number
};

struct parser_state
{
    struct inifile_data context;
    struct inifile_section* section_cursor;
    struct inifile_entry* entry_cursor;
    char buffer[255];
    int buffer_cursor;
};

typedef enum parser_states (*ini_file_state_transition)(char next_char, struct parser_state* pastate);

enum parser_states comment_state(char next_char, struct parser_state* pastate)
{
    if(next_char == '\n')
    {
        pastate->buffer_cursor = 0;
    return linestart;
    }
    return comment;
}

enum parser_states linestart_state(char next_char, struct parser_state* pastate)
{
    if(next_char == '\n')
    {
        pastate->buffer_cursor = 0;
    return linestart;
    }
    if(next_char == ';')
    {
        pastate->buffer_cursor = 0;
        return comment;
    }
    if(next_char == ']' || next_char == '=')
    {
        // this is error, discard the string
        pastate->buffer_cursor = 0;
        return comment;
    }
    if(isspace(next_char))
        return linestart;
    if(next_char == '[')
    {
        pastate->buffer_cursor = 0;
    return sectionentry;
    }
    // in all other cases, start of entry
    if(pastate->section_cursor == NULL)
    {
        //entries without section are not considered
        pastate->buffer_cursor = 0;
    return comment;
    }
    pastate->buffer[pastate->buffer_cursor++] = next_char;
    return entryname;    
}

enum parser_states sectionentry_state(char next_char, struct parser_state* pastate)
{
    if(next_char == '\n')
    {
        // this is an error; data are discarded
        pastate->buffer_cursor = 0;
    return linestart;
    }
    if(next_char == '[' || next_char == ';')
        return comment;

    if(next_char != ']')
        pastate->buffer[pastate->buffer_cursor++] = next_char;
    // protection from buffer overflow
    if(next_char == ']' || pastate->buffer_cursor > 254)
    {
        // this is section name's end
    // if we have non-empty name, create new section
    if(pastate->buffer_cursor > 0)
    {
            // prepare next section
            if(pastate->section_cursor == NULL)
            {
                // make first section
                pastate->context.first_section = (struct inifile_section*)malloc(sizeof(struct inifile_section));
                init_section(pastate->context.first_section);
                pastate->section_cursor = pastate->context.first_section;
            }
            else
            {
                pastate->section_cursor->next_section = (struct inifile_section*)malloc(sizeof(struct inifile_section));
                init_section(pastate->section_cursor->next_section);
                pastate->section_cursor = pastate->section_cursor->next_section;
            }
            pastate->entry_cursor = NULL;
        pastate->section_cursor->section_name = (char*)malloc(pastate->buffer_cursor + 1);
        memcpy((void*)pastate->section_cursor->section_name, pastate->buffer, pastate->buffer_cursor);
        *(char*)(pastate->section_cursor->section_name + pastate->buffer_cursor) = '\0';
    }
    // discard the rest of the string
    return comment;
    }

    return sectionentry;

}

enum parser_states entryname_state(char next_char, struct parser_state* pastate)
{
    if(next_char == '\n')
    {
        // this is an error; data are discarded
        pastate->buffer_cursor = 0;
    return linestart;
    }
    if(next_char == ';' || next_char == '[' || next_char == ']')
    {
        // this is an error; data are discarded
        pastate->buffer_cursor = 0;
    return comment;
    }
    if(next_char != '=')
        pastate->buffer[pastate->buffer_cursor++] = next_char;
    // protection from buffer overflow
    if(pastate->buffer_cursor > 254)
    {
        // too long, discard everything
        pastate->buffer_cursor = 0;
    return comment;
    }

    if(next_char == '=')
    {
        // make new entry
        if(pastate->entry_cursor == NULL)
        {
            // make first entry
            pastate->section_cursor->first_entry = (struct inifile_entry*)malloc(sizeof(struct inifile_entry));
            init_entry(pastate->section_cursor->first_entry);
            pastate->entry_cursor = pastate->section_cursor->first_entry;
        }
        else
        {
            pastate->entry_cursor->next_entry = (struct inifile_entry*)malloc(sizeof(struct inifile_entry));
            init_entry(pastate->entry_cursor->next_entry);
        pastate->entry_cursor = pastate->entry_cursor->next_entry;
        }
        pastate->entry_cursor->entry_name = (char*)malloc(pastate->buffer_cursor + 1);
        memcpy((void*)pastate->entry_cursor->entry_name, pastate->buffer, pastate->buffer_cursor);
        *(char*)(pastate->entry_cursor->entry_name + pastate->buffer_cursor) = '\0';
        pastate->buffer_cursor = 0;
    return entryvalue;
    }
    return entryname;
}

enum parser_states entryvalue_state(char next_char, struct parser_state* pastate)
{
    enum parser_states res = entryvalue;
    if(next_char == '\n')
    {
        res = linestart;
    }
    else if(next_char == ';')
    {
        res = comment;
    }
    else
    {
        pastate->buffer[pastate->buffer_cursor++] = next_char;
    if(pastate->buffer_cursor > 254)
        res = comment;
    }
    if(res != entryvalue)
    {
        pastate->entry_cursor->entry_value = (char*)malloc(pastate->buffer_cursor + 1);
        memcpy((void*)pastate->entry_cursor->entry_value, pastate->buffer, pastate->buffer_cursor);
        *(char*)(pastate->entry_cursor->entry_value + pastate->buffer_cursor) = '\0';
        pastate->buffer_cursor = 0;
    }

    return res;

}

int open_ini_file(const char* fname, pinifile_data* pinidb)
{
    if(fname == NULL || pinidb == NULL)
        return 2;

    FILE* fstream = fopen(fname, "r");
    if(!fstream)
        return 1;

    struct parser_state context;
    memset(&context, 0, sizeof(struct parser_state));
    int character = 0, state_index = linestart;
    ini_file_state_transition parse_functions_table[] = {
        comment_state,
    linestart_state,
    sectionentry_state,
    entryname_state,
    entryvalue_state
    };
    do
    {
        character = fgetc(fstream);
    if(character != EOF)
    {
        state_index = parse_functions_table[state_index](character, &context);
    }
    } while(!(feof(fstream) || ferror(fstream)));
    fclose(fstream);

    *pinidb = malloc(sizeof(struct inifile_data));
    // here we are copying a pointer, yes
    memcpy(*pinidb, &context.context, sizeof(struct inifile_data));

    return 0;    
}

const char* get_value(const pinifile_data inidb, const char* section, const char* entry)
{
    struct inifile_data* pinidb = (struct inifile_data*)inidb;
    if(pinidb == NULL || section == NULL || entry == NULL)
        return NULL;

    struct inifile_section* pcur_s = NULL;
    struct inifile_entry* pcur_e = NULL;

    pcur_s = pinidb->first_section;
    while(pcur_s != NULL)
    {
        pcur_e = pcur_s->first_entry;

    if(!strcmp(section, pcur_s->section_name))
            while(pcur_e != NULL)
            {
                if(!strcmp(entry, pcur_e->entry_name))
                    return pcur_e->entry_value;
                pcur_e = pcur_e->next_entry;
            }
        pcur_s = pcur_s->next_section;
    }
    return NULL;

}

int get_integer(const pinifile_data inidb, const char* section, const char* entry, int default_value)
{
    const char* evalue = get_value(inidb, section, entry);
    if(!evalue)
        return default_value;
    return strtol(evalue, NULL, 10);
}

double get_real(const pinifile_data inidb, const char* section, const char* entry, double default_value)
{
    const char* evalue = get_value(inidb, section, entry);
    if(!evalue)
        return default_value;
    return strtod(evalue, NULL);

}

pinifile_data create_inidb()
{
    void* result = malloc(sizeof(struct inifile_data));
    memset(result, 0, sizeof(struct inifile_data));
    // Here we are copying a pointer, yes
    return result;
}

void set_entry(pinifile_data inidb, const char* section, const char* entry, const char* value)
{
    struct inifile_data* pinidb = (struct inifile_data*)inidb;
    if(pinidb == NULL || section == NULL || entry == NULL || value == NULL)
        return;

    struct inifile_section* target_section = NULL,* section_cursor = NULL;

    section_cursor = pinidb->first_section;
    while(section_cursor != NULL)
    {
        if(!strcmp(section, section_cursor->section_name))
    {
        target_section = section_cursor;
        break;
    }
    if(NULL == section_cursor->next_section)
        break;
        section_cursor = section_cursor->next_section;
    }
    if(target_section == NULL)
    {
         // prepare next section
         if(section_cursor == NULL)
         {
             // make first section
             pinidb->first_section = (struct inifile_section*)malloc(sizeof(struct inifile_section));
             init_section(pinidb->first_section);
             target_section = pinidb->first_section;
         }
         else
         {
             section_cursor->next_section = (struct inifile_section*)malloc(sizeof(struct inifile_section));
             init_section(section_cursor->next_section);
             target_section = section_cursor->next_section;
         }
         char* sname = (char*)malloc(strlen(section) + 1);
         target_section->section_name = strcpy(sname, section);
    }

    struct inifile_entry* target_entry = NULL,* entry_cursor = NULL;
    entry_cursor = target_section->first_entry;
    if(entry_cursor != NULL)
    {
        for(; entry_cursor->next_entry != NULL; entry_cursor = entry_cursor->next_entry)
        if(!strcmp(entry, entry_cursor->entry_name))
        {
            target_entry = entry_cursor;
        break;
        }
    }
    if(target_entry == NULL)
    {
        // prepare next entry
        if(entry_cursor == NULL)
        {
            // make first entry
            target_section->first_entry = (struct inifile_entry*)malloc(sizeof(struct inifile_entry));
            init_entry(target_section->first_entry);
            target_entry = target_section->first_entry;
        }
        else
        {
            entry_cursor->next_entry = (struct inifile_entry*)malloc(sizeof(struct inifile_entry));
            init_entry(entry_cursor->next_entry);
            target_entry = entry_cursor->next_entry;
        }
        char* ename = (char*)malloc(strlen(entry) + 1);
        target_entry->entry_name = strcpy(ename, entry);
    }
    if(target_entry->entry_value != NULL)
        free((void*)target_entry->entry_value);

    char* eval = (char*)malloc(strlen(value) + 1);
    target_entry->entry_value = strcpy(eval, value);
}

int dump_ini_file(const char* fname, pinifile_data inidb)
{
    struct inifile_data* pinidb = (struct inifile_data*)inidb;
    if(fname == NULL || pinidb == NULL)
        return 1;
    FILE* fstream = fopen(fname, "w");
    if(!fstream)
        return 1;

    struct inifile_section* section_cursor = pinidb->first_section;
    while(section_cursor != NULL)
    {
        fprintf(fstream, "[%s]\n", section_cursor->section_name);
    struct inifile_entry* entry_cursor = section_cursor->first_entry;
    while(entry_cursor != NULL)
    {
        fprintf(fstream, "%s=%s\n", entry_cursor->entry_name, entry_cursor->entry_value);
        entry_cursor = entry_cursor->next_entry;
    }
        section_cursor = section_cursor->next_section;
    }
    fclose(fstream);
    return 0;
}
EN

回答 2

Code Review用户

回答已采纳

发布于 2015-10-21 13:54:55

从快速的观察来看,我看到了一些可以改进的东西:

  • 您的代码有许多不必要的类型转换。特别地,向void*转换不仅毫无用处,而且也很危险,因为它可能隐藏错误,就像将值传递给期望指针的函数一样。当您调用void*memcpy等时,不要显式地将其转换为free
  • 在C中,从void*转换到T*也是不必要的。void*隐式转换是两种方式。除非您真的关心与C和C++编译相同的代码,否则不需要像从malloc返回值这样的地方进行强制转换。
  • 您可以在这样的地方保存类型名称的重复: pinifile_data create_inidb() { void* inifile_data=malloc(Inifile_data);memset(结果,0,sizeof(struct inifile_data));//这里我们正在复制指针,是返回结果;}通过在sizeof表达式中使用变量名。sizeof是编译时的,所以它是合法的.例如: pinifile_data create_inidb( inifile_data* result ){ struct inifile_data*result=malloc(sizeof of (*inifile_data*));memset(结果0,sizeof of(*inifile_data*));返回结果;}利用您的小类型推断,不要重复自己。如果您更改了该类型的名称,那么现在它只是该函数中更新的一个地方。但是更进一步,既然这个函数想要分配零初始化内存,为什么不使用calloc()呢?pinifile_data create_inidb(void) {返回calloc(1,sizeof(struct inifile_data));}
  • 我个人认为用struct为结构的每一个实例化加上不切实际的前缀,所以我总是在声明中对它们进行类型化,以避免这种情况。enums也是如此。
  • 您的代码中有一些格式化问题,影响了可读性。像Clang格式这样的工具可以自动修复这个问题。
  • 在单行语句中添加大括号{ }是可选的,但这是个好主意。它将使您的代码的未来更新更少痛苦和更少的错误发生。
票数 4
EN

Code Review用户

发布于 2015-10-21 11:51:06

set_entry函数太大了。把它重构成几个。

节,条目,值不能是NULL,所以最好使用const引用。

open_ini_file返回1,2,0,它们是什么意思?使用人类可读的定义或枚举。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/108264

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档