美文网首页
C语言解析配置文件的函数

C语言解析配置文件的函数

作者: boldcautious | 来源:发表于2019-05-22 16:17 被阅读0次

本文示例可以解析的配置文件为key-value格式,key与value之间可以使用空白符、等号及冒号分隔。

配置文件示例如下:

# test.conf
nameserver 114.114.114.114
nameserver 8.8.8.8
DOMAIN=lab.foo.com 
domain=bar.foo.com
search lab.foo.com
search bar.foo.com lab.example.com
DNS1=114.114.114.114 
DNS2=8.8.8.8
test:/usr/bin:/root/bin
test:/home/test:/bin/bash

数据结构定义的头文件:

// config.h

/* 该配置项只能有一个,重复会报错 */
#define CONF_SINGLE       1
/* 该配置项可以设置多个且都有效,如设置DNS的nameserver */
#define CONF_MULTIPLE     2
/* 配置文件中,第一个配置项有效,忽略后面重复的配置项 */
#define CONF_FIRST_VALID  3
/* 配置文件中,最后一个配置项有效,忽略前面重复的配置项 */
#define CONF_LAST_VALID   4

#define MAX_BUF_SIZE      1024

typedef struct conf_s
{
    const char *key; /* conf keyword */
    const int   type;
    const char *connector; /* join fields by connector characters */
    char       *value;
} conf_t;

int parse(FILE *fp, conf_t *cf);

解析函数实现:

// config.c

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

#include "config.h"

char *trim_left_right(char *s)
{
    char *e;

    /* 去除开头的空白 */
    while (isspace(*s)) s++;

    /* 结尾空白全部置为\0 */
    e = s + strlen(s) - 1;
    while (isspace(*e) && e > s) {
        *e = '\0';
        e--;
    }
    if (e == s) {
        *e = '\0';
    }

    return s;
}

int isdelimiter(char c)
{
    if (isspace(c) || c == '=' || c == ':' ) {
        return 1;
    } else {
        return 0;
    }
}

int set_conf(conf_t *conf, char *key, char *value)
{
    conf_t *p;

    if (key == NULL || value == NULL || conf == NULL) {
        return 0;
    }

    for (p = conf; p->key != NULL; p++)
    {
        if (strcasecmp(key, p->key) == 0 && p->value) {
            switch (p->type) {
            case CONF_MULTIPLE:
                if (*p->value != '\0') {
                    /* 存在重复项时,使用connector拼接 */
                    sprintf(p->value, "%s%s%s", p->value, p->connector, value);
                } else {
                    sprintf(p->value, "%s", value);
                }
                break;
            case CONF_SINGLE:
                if (*p->value != '\0') {
                    printf("ERROR: keyword \"%s\" is duplicate\n", key);
                    return 0;
                } else {
                    sprintf(p->value, "%s", value);
                }
                break;
            case CONF_FIRST_VALID:
                if (*p->value == '\0') {
                    sprintf(p->value, "%s", value);
                }
                break;
            case CONF_LAST_VALID:
                sprintf(p->value, "%s", value);
                break;
            }

            return 1;
        }
    }
    printf("ERROR: unknown keyword \"%s\"\n", key);
    return 0;
}

int parse(FILE *fp, conf_t *cf)
{
    char buf[MAX_BUF_SIZE], *key, *value, *tmp;

    memset(buf, 0, MAX_BUF_SIZE);

    while (fgets(buf, MAX_BUF_SIZE, fp) != NULL)
    {
        /* 去除#号及其之后的字符 */
        tmp = buf;
        while (*tmp != '#' && *tmp != '\0') {
            tmp++;
        }
        if (*tmp == '#') {
            *tmp = '\0';
        }
        
        /* 去除前后的空白符 */
        key = trim_left_right(buf);

        if (*key == '\0') {
            memset(buf, 0, MAX_BUF_SIZE);
            continue;
        }

        /* 使用\0设置key和value之间的分隔符,即可取出key,并得到value的起始位置 */
        value = key;
        while (!isdelimiter(*value) && *value != '\0') {
            value++;
        }
        while (isdelimiter(*value) && *value != '\0') {
            *value = '\0';
            value++;
        }

        if (*value == '\0') {
            printf("ERROR: no value for keyword \"%s\"\n", key);
            memset(buf, 0, MAX_BUF_SIZE);
            continue;
        }
        
        if (!set_conf(cf, key, value)) {
            return 0;
        }

        memset(buf, 0, MAX_BUF_SIZE);
    }

    return 1;
}

解析函数调用及测试:

// main.c

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

#include "config.h"

#define MAX_VALUE_SIZE    64

int main(int argc, char *argv[])
{
    int i;
    FILE *fp;
    char *conffile;
    char value[6][MAX_VALUE_SIZE];
    conf_t conf[] = {
        {"nameserver", CONF_MULTIPLE, " ", value[0]},
        {"domain", CONF_LAST_VALID, NULL, value[1]},
        {"search", CONF_LAST_VALID, NULL, value[2]},
        {"dns1", CONF_SINGLE, NULL, value[3]},
        {"dns2", CONF_SINGLE, NULL, value[4]},
        {"test", CONF_FIRST_VALID, NULL, value[5]},
        {NULL, 0, NULL, NULL}
    };

    if (argc == 2) {
        conffile = argv[1];
    } else {
        conffile = "test.conf";
    }

    if ((fp = fopen(conffile, "r")) == NULL)
    {
        fprintf(stderr, "ERROR: can't open conf file \"%s\"\n", conffile);
        exit(1);
    }

    for (i=0; i<6; i++) {
        memset(value[i], 0, MAX_VALUE_SIZE);
    }

    if (!parse(fp, conf)) {
        fclose(fp);
        exit(1);
    }

    for (i=0; i<6; i++) {
        if (strlen(conf[i].value)) {
            printf("%s: [%s]\n", conf[i].key, conf[i].value);
        }
    }

    fclose(fp);
    return 0;
}

Makefile:

LIBS= -lm
CFLAGS= -Wall -O2 -g

OBJS= main.o config.o

main: $(OBJS)
    gcc -o $@ $(OBJS) $(LIBS)

clean:
    rm -f main $(OBJS)

编译及测试:

# make
cc -Wall -O2 -g   -c -o main.o main.c
cc -Wall -O2 -g   -c -o config.o config.c
gcc -o main main.o config.o -lm
# ./main 
nameserver: [114.114.114.114 8.8.8.8]
domain: [bar.foo.com]
search: [bar.foo.com lab.example.com]
dns1: [114.114.114.114]
dns2: [8.8.8.8]
test: [/usr/bin:/root/bin]

相关文章

  • C语言解析配置文件的函数

    本文示例可以解析的配置文件为key-value格式,key与value之间可以使用空白符、等号及冒号分隔。 配置文...

  • C语言配置文件解析库——iniparser

    C语言配置文件解析库——iniparser 前言:在对项目的优化时,发现Linux下没有专门的供给C语言使用的配置...

  • iOS开发 -- C语言基础3(函数)

    iOS开发 -- C语言基础3(函数) 什么是函数 一、函数的分类 C语言中的函数就是面向对象中的"方法",C语言...

  • 是否还需要在源文件中添加“extern C”?

    c++调用c语言的函数时我们一般在c语言函数声明处用extern "C"包c函数。例如: extern "C"的主...

  • gcc

    C语言 c语言是面向函数的语言,所以c语言中全部都是函数 看看单文件中对函数的生命和调用。 要先对函数进行声明再进...

  • 2018-08-29日常C笔记

    C语言初学笔记 C语言程序是由函数组成的C语言程序启动时,系统就会自动调用名字叫main的函数C语言规定了函数定义...

  • Redis 源码简洁剖析 02 - SDS 字符串

    C 语言的字符串函数 C 语言 string 函数[https://devdocs.io/c-strings/],...

  • C++ 读取JSON配置文件

    C++ 读取JSON配置文件 前言 JSON作为当下流行的数据交换语言,其易于人阅读和编写,易于机器解析和生成,以...

  • 函数变参的使用

    介绍C/C++,java,和go语言对函数变参的使用。 1. C/C++语言的变参 1.1 变参函数声明 变参函数...

  • nacos源码4-spring客户端

    一 xml支持 1.1 相关配置文件 1.2 NacosNamespaceHandler 注册xml配置解析函数 ...

网友评论

      本文标题:C语言解析配置文件的函数

      本文链接:https://www.haomeiwen.com/subject/czyuzqtx.html