在实际项目中,经常会把软件的某些选项写入配置文件。 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();
下面是源文件:
- #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 已经运用在实际的项目中,目前为止没发现什么问题。