当前位置: 首页 > 文档资料 > C 语言程序设计 >

配置文件解析

优质
小牛编辑
137浏览
2023-12-01

C语言面向对象编程(六):配置文件解析

在实际项目中,经常会把软件的某些选项写入配置文件。 Windows 平台上的 INI 文件格式简单易用,本篇文章利用《C语言面向对象编程(五):单链表实现》中实现的单链表,设计了一个“类” ini_parser 来读写 INI 格式的配置文件。

struct ini_parser 可以解析 INI 格式的字符串、文件,也可以将内存中的符合 INI 格式的数据写入文件,能够支持 Windows 、 Linux 、 Android 等多平台。目前暂不支持选项分组功能。

功能相对简单,直接看源码吧。

下面是头文件:

struct single_list;  

struct ini_parser {  
    struct single_list * keyvalues;  
    int (*parse_file)(struct ini_parser *, const char * file);  
    int (*parse_string)(struct ini_parser *, const char *text);  
    char * (*value)(struct ini_parser *, const char * key);  
    void (*set_value)(struct ini_parser *, const char * key, const char * value);  
    void (*remove)(struct ini_parser *, const char *key);  
    int (*save_to_file)(struct ini_parser *, const char * file);  
    void (*deletor)(struct ini_parser *ini);  
};  

struct ini_parser * new_ini_parser();

struct init_parser 的声明符合我们在本系列文章中提到的面向对象框架,需要说明的是,一旦 deletor 方法被调用, ini_parser 的实例将不再允许访问。

下面是源文件:

#include "ini_parser.h"  
#include <stdio.h>  
#include <string.h>  

struct tag_value_pair{  
    struct slist_node node;  
    char * szTag;  
    char * szValue;  
};  
typedef struct tag_value_pair tag_value;  

static void _tag_value_free(struct slist_node *node)  
{  
    if(node) delete_tag_value_pair(node);  
}  

static int _tag_value_hittest(struct slist_node * node, void *key)  
{  
    return strcmp((char*)tag, ((struct tag_value_pair*)node)->szTag);  
}  

static struct single_list * new_tag_value_list()  
{  
    return new_single_list(_tag_value_free, _tag_value_hittest);  
}  

static struct tag_value_pair *new_tag_value_pair()  
{  
    struct tag_value_pair * pair = (struct tag_value_pair *)malloc(sizeof(struct tag_value_pair));  
    pair->node.next = 0;  
    pair->szTag = 0;  
    pair->szValue = 0;  
    return pair;  
}  

static struct tag_value_pair * make_tag_value_pair(char * tag, char * value)  
{  
    struct tag_value_pair *pair = 0;  
    if(!tag || !value)return 0;  

    pair = (struct tag_value_pair*)malloc(sizeof(struct tag_value_pair));  
    pair->szTag = strdup(tag);  
    pair->szValue = strdup(value);  
    pair->node.next = 0;  
    return pair;  
}  


static struct tag_value_pair * parse_line(char *line, int len)  
{  
    struct tag_value_pair * pair = 0;  
    int count = 0;  
    char * p = line;  
    char * end = 0;m  
    char * start = line;  
    if(!p) return 0;  
    while(*p == ' ') ++p;  


    /*blank line*/  
    if(p - line == len ||  
            *p == '\r' ||  
            *p == '\n' ||  
            *p == '\0') return 0;  

    /*do not support group*/  
    if(*p == '[') return 0;  
    /*comments*/  
    if(*p == '#') return 0;  

    /* extract key */  
    start = p;  
    end = line + len;  
    while(*p != '=' && p!= end) ++p;  
    if(p == end)  
    {  
        /* none '=' , invalid line */  
        return 0;  
    }  
    end = p - 1;  
    while(*end == ' ') --end; /* skip blank at the end */  
    count = end - start + 1;  

    pair = new_tag_value_pair();  
    pair->szTag = malloc(count + 1);  
    strncpy(pair->szTag, start, count);  
    pair->szTag[count] = 0;  

    /* extract value */  
    ++p;  
    end = line + len; /* next pos of the last char */  
    while( *p == ' ' && p != end) ++p;  
    if(p == end)  
    {  
        delete_tag_value_pair(pair);  
        return 0;  
    }  
    start = p;  
    --end; /* to the last char */  
    if(*end == '\n') { *end = 0; --end; }  
    if(*end == '\r') { *end = 0; --end; }  
    count = end - start + 1;  
    if(count > 0)  
    {  
        pair->szValue = malloc(count + 1);  
        strncpy(pair->szValue, start, count);  
        pair->szValue[count] = 0;  
    }  

    /* release empty key-value pair */  
    if(!pair->szValue)  
    {  
        delete_tag_value_pair(pair);  
        return 0;  
    }  

    return pair;  
}  

static int _parse_file(struct ini_parser * ini, const char *file){  
    FILE * fp = fopen(file, "r");  
    if(fp)  
    {  
        struct tag_value_pair * pair = 0;  
        char buf[1024] = {0};  
        while(fgets(buf, 1024, fp))  
        {  
            pair = parse_line(buf, strlen(buf));  
            if(pair)  
            {  
                ini->keyvalues->add(ini->keyvalues, pair);  
            }  
        }  
        fclose(fp);  
        return ini->keyvalues->size;  
    }  
    return -1;  
}  

static int _parse_text(struct ini_parser * ini, const char * text){  
    char *p = text;  
    char * start = 0;  
    struct tag_value_pair * pair = 0;  
    if(!text) return -1;  

    while(1)  
    {  
        start = p;  
        while(*p != '\n' && *p != '\0' )++p;  
        if(*p == '\0') break;  

        pair = parse_line(start, p - start);  
        if(pair) ini->keyvalues->add(ini->keyvalues, pair);  

        ++p;  
    }  

    return ini->keyvalues->size;  
}  

static char * _value(struct ini_parser * ini, const char * key){  
    struct tag_value_pair * pair = NODE_T(ini->keyvalues->find_by_key(ini->keyvalues, key), struct tag_value_pair);  
    if(pair) return pair->szValue;  
    return 0;  
}  

static void _set_value(struct ini_parser * ini, const char * key, const char *value){  
    struct tag_value_pair * pair = NODE_T(ini->keyvalues->find_by_key(ini->keyvalues, key), struct tag_value_pair);  
    if(pair)  
    {  
        if(pair->szValue) free(pair->szValue);  
        pair->szValue = strdup(value);  
    }  
    else  
    {  
        ini->keyvalues->add(ini->keyvalues, make_tag_value_pair(key, value));  
    }  
}  

static void _remove(struct ini_parser * ini, const char * key){  
    struct tag_value_pair * pair = NODE_T(ini->keyvalues->find_by_key(ini->keyvalues, key), struct tag_value_pair);  
    if(pair)ini->keyvalues->remove(ini->keyvalues, pair);  
}  

static void write_keyvalue(struct tag_value_pair * pair, FILE *fp)  
{  
    fputs(pair->szTag, fp);  
    fputc('=', fp);  
    fputs(pair->szValue, fp);  
    fputc('\n', fp);  
}  

static int _save_to_file(struct ini_parser * ini, const char * file){  
    if(ini->keyvalues->size > 0)  
    {  
        FILE * fp = fopen(file, "w");  
        if(fp)  
        {  
            struct tag_value_pair * pair = NODE_T(ini->keyvalues->head,struct tag_value_pair);  
            while(pair != 0)  
            {  
                write_keyvalue(pair, fp);  
                pair = NODE_T(pair->node.next, struct tag_value_pair);  
            }  

            fclose(fp);  
            return 0;  
        }  
    }  
    return -1;  
}  

static void _delete_ini_parser(struct ini_parser *ini){  
    if(ini)  
    {  
        ini->keyvalues->deletor(ini->keyvalues);  
        free(ini);  
    }  
}  

struct ini_parser * new_ini_parser(){  
    struct ini_parser * ini = (struct ini_parser*)malloc(sizeof(struct ini_parser));  
    ini->keyvalues = new_tag_value_list();  
    ini->parse_file = _parse_file;  
    ini->parse_string = _parse_text;  
    ini->value = _value;  
    ini->set_value = _set_value;  
    ini->remove = _remove;  
    ini->save_to_file = _save_to_file;  
    ini->deletor = _delete_ini_parser;  
    return ini;  
}

下面是简单的测试代码:

static char * g_szIniString = "#abc\nfirst=2\nsecond\nname=charli  zhang \n";  

static void ini_parser_test_string()  
{  
    struct ini_parser * ini = new_ini_parser();  
    int size = ini->parse_string(ini, g_szIniString);  

    assert( size > 0);  
    assert( ini->value(ini, "second") == 0 );  
    assert( ini->value(ini, "abc") == 0);  
    assert( ini->value(ini, "name") != NULL );  
    assert( ini->value(ini, "first") != NULL);  

    printf("ini string: %s\n", g_szIniString);  
    printf("key-value pairs count = %d\n", size);  
    printf("key \'name\'', value = %s\n", ini->value(ini, "name"));  
    printf("key \'first\'', value = %s\n", ini->value(ini, "first"));  

    ini->set_value(ini, "baidu", "hahaha");  
    ini->save_to_file(ini, "write.conf");  

    ini->remove(ini, "first");  
    ini->save_to_file(ini, "write2.conf");  

    ini->deletor(ini);  
}  

static void ini_parser_test_file()  
{  
    struct ini_parser * ini = new_ini_parser();  
    int size = ini->parse_file(ini, "test.conf");  

    assert( size > 0);  
    assert( ini->value(ini, "second") == 0 );  
    assert( ini->value(ini, "abc") == 0);  
    assert( ini->value(ini, "name") != NULL );  
    assert( ini->value(ini, "first") != NULL);  

    printf("ini string: %s\n", g_szIniString);  
    printf("key-value pairs count = %d\n", size);  
    printf("key \'name\'', value = %s\n", ini->value(ini, "name"));  
    printf("key \'first\'', value = %s\n", ini->value(ini, "first"));  
    printf("key \'baidu\'', value = %s\n", ini->value(ini, "baidu"));  

    ini->deletor(ini);  
}  

void ini_parser_test()  
{  
    ini_parser_test_string();  
    ini_parser_test_file();  
}

struct ini_parser 已经运用在实际的项目中,目前为止没发现什么问题。