C(Unix)中基于文件的配置处理

时间:2009-01-04 23:41:37

标签: c parsing unix configuration

这可能是编程时最常见的任务/问题之一;您需要在某处存储应用程序的配置。

在我尝试创建Web服务器或其他应用程序时,我希望尽可能保持代码清洁,因为我对编程的主要兴趣是架构。这导致我希望将配置存储在一个文件中,无需重新编译软件即可对其进行更改。

我不是来重新发明轮子或类似的东西,所以我想做的是在C on * nix上创建一个配置阅读器。配置看起来可能与任何其他软件的配置非常相似; Apache,vsftpd,MySQL等。

基本问题是:如何从文本文件中读取并有效地处理每一行(在纯C中)?我是否需要使用fgetc()并处理每个字符?

12 个答案:

答案 0 :(得分:13)

嗯有LibConfig

我在Wiki

中找到了它

答案 1 :(得分:12)

各种各样的人给出了相当不错的建议 - Pure-FTP示例很有意思。

您还应该阅读E S Raymond撰写的TAOUP(Unix编程艺术)。它有一些示例 - 大量的配置文件。它还概述了配置文件的规范习语。例如,您应该允许'#'在行的末尾开始注释,并忽略空行。如果配置文件包含您不理解的行 - 是否要忽略并继续静默,或者是否要抱怨,您还应该决定要执行的操作。 (我更喜欢抱怨的东西;然后我知道为什么我刚刚添加的东西没有任何影响 - 而沉默忽略意味着我不知道我刚添加的条目是否有任何影响。)

另一个问题是找到配置文件。您是通过编译位置,使用环境变量覆盖的默认安装位置,还是通过其他一些魔法来实现的?确保有一个命令行选项允许绝对指定配置文件 - 甚至考虑将其作为唯一的方法。

否则,在广泛的范围内,保持简单,每个人都会更快乐。

答案 2 :(得分:9)

好的,让我们点击其他部分。你需要考虑你想要的“语言”。在UNIX世界中,规范版本可能是以空格分隔的文本(思考/etc/hosts)或“:”分隔文本(如/etc/passwd)。

你有几个选择,在某种意义上最简单的就是使用scanf(3)。再次,请阅读手册页以获取详细信息,但是如果行条目类似于

port    100

然后你会找到像

这样的东西
char inbuf[MAXLINE];
int  val;

scanf("%s %d\n", &inbuf[0], &val);

如果你编写一个简单的FSA解析,你可以获得更大的灵活性:从行中一次读取一个字符,并使用有限自动机来定义要做的事情。

答案 3 :(得分:6)

有很多方法。你需要使用fgetc。您应该阅读stdio手册页,但规范的事情是用fopen(3)打开文件,然后使用fgets(3)读取一次读取一行。这看起来像是:

#include <stdio.h>

FILE * fp ;
char bufr[MAXLINE];

if((fp = fopen(filename, "r") != NULL){
    while(! feof(fp)){
         fgets(bufr, MAXLINE, fp);
         /* Do stuff */
    }
} else {
    /* error processing, couldn't open file */
}

您还可以在Sourceforge查看libini

答案 4 :(得分:6)

好的,这是一个C代码的真实例子:

/* demo-fgets -- read a "demo.text", copy to stdout with line
   numbers. */

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

#define MAXLINE 100

FILE * fp;
char bufr[MAXLINE];

extern int errno ;

int main(int argc, char ** argv){
    int count = 0 ;
    if((fp = fopen("demo.text","r")) != NULL){
        /* then file opened successfully. */
        while(fgets(bufr,MAXLINE,fp)!=NULL){
            /* then no read error */
            count +=1;
            printf("%d: %s",     /* no "\n", why? */
                   count, bufr);
        }
        /* fgets returned null */
        if(errno != 0){
            perror(argv[0]);    /* argv[0]?  Why that? */
            exit(1);
        }
        exit(0);                /* EOF found, normal exit */
    } else {                    /* there was an error on open */
        perror(argv[0]);
        exit(1);
    }
}

我用这个输入文件运行它:

520 $ cat demo.text 
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum
aliquet augue id quam. Sed a metus. Quisque sit amet quam. Sed id
ante. In egestas est non mi. Sed vel velit non elit vehicula
viverra. Curabitur eget tortor in ipsum vulputate
faucibus. Suspendisse imperdiet mauris at nibh. Sed interdum. Maecenas
vulputate, massa vel placerat mattis, ante est tincidunt sem, in
sollicitudin velit lacus non tortor. Etiam sagittis consequat nisl. 

Vestibulum id leo quis mauris gravida placerat. Donec aliquet justo a
tortor. Etiam nisi nibh, auctor non, luctus et, aliquam vitae,
metus. Cum sociis natoque penatibus et magnis dis parturient montes,
nascetur ridiculus mus. Nunc lacinia quam a ligula. Nulla quis nisi eu
nunc imperdiet cursus. Nunc vitae nisi vitae tellus posuere
sollicitudin. Nunc suscipit, dui ac interdum euismod, pede nisl varius
dui, sed mattis libero mauris eu felis. Nam mattis dui eget
nunc. Suspendisse malesuada, pede eget posuere pellentesque, neque
eros pretium nibh, ut blandit dui leo dapibus orci. Etiam lacinia
lectus at orci. Donec ligula lacus, sagittis nec, sodales et,
fringilla lobortis, eros. Etiam sit amet nulla. Aliquam mollis pede id
enim. Etiam ligula felis, pulvinar nec, vestibulum molestie, interdum
ut, urna. Ut porta ullamcorper diam. Nullam interdum arcu. 

Pellentesque habitant morbi tristique senectus et netus et malesuada
fames ac turpis egestas. Etiam eu enim quis sem accumsan
tristique. Proin non sem. Etiam quis ante. Aenean ornare pellentesque
dolor. Praesent sodales. Cras dui velit, scelerisque a, accumsan a,
vestibulum in, dui. Pellentesque sed sapien. Etiam augue est,
convallis eget, egestas vel, molestie id, turpis. Cum sociis natoque
penatibus et magnis dis parturient montes, nascetur ridiculus
mus. Cras posuere lorem eu diam. Ut ultricies velit. Nunc imperdiet
suscipit mauris. Vestibulum molestie elit id risus. Phasellus et
purus. Vestibulum id mauris. Fusce gravida elit quis turpis. Aliquam
ut est. 

Sed in mauris eu nulla rhoncus suscipit. Nam id dolor sit amet turpis
placerat sodales. Nunc ipsum. Quisque diam tellus, dapibus non,
interdum at, aliquam sit amet, tellus. Donec non pede eget massa
aliquam semper. Quisque dictum lacinia ipsum. Fusce magna purus,
mattis id, commodo et, lobortis eu, arcu. Vestibulum viverra neque a
nulla. Cum sociis natoque penatibus et magnis dis parturient montes,
nascetur ridiculus mus. Pellentesque vel felis in ligula blandit
auctor. Quisque quam. Curabitur turpis. Morbi molestie augue a
nisi. Nulla sollicitudin sagittis elit. Suspendisse in odio sed magna
dictum vestibulum. Duis facilisis lorem eget neque. Proin sit amet
urna eget velit scelerisque aliquam. Pellentesque imperdiet. Nullam
sapien. Nullam placerat ipsum eget metus. 

Mauris ornare risus eu velit. Morbi bibendum diam in sem. Morbi
aliquet nisl sit amet quam. Donec ornare sagittis nibh. Fusce ac
lectus. Sed sit amet risus. Integer facilisis commodo
sem. Pellentesque facilisis. Donec libero. Lorem ipsum dolor sit amet,
consectetur adipiscing elit.

答案 5 :(得分:5)

为什么你永远从头开始编写这个代码,它已经多次完成了所以;找到一个好的F / OSS实现并使用它。

  

如何从文本文件中读取并有效地处理每一行

不要担心效率,读取配置文件无关紧要。转而采用简单性和可维护性。

答案 6 :(得分:3)

又一个解决方案 - Pure-ftpd

  

与许多守护进程不同,Pure-FTPd不会读取任何配置文件。相反,它使用命令行选项。 ...在服务器中添加配置文件的解析器是不好的   理念。它减慢了一切,无需资源。

对于选项,有getopt

答案 7 :(得分:3)

我会这样做的方式(伪代码):

while(there is input) {
  get one line;
  if (it is empty line || it beings with spaces followed by a '#') {
    skip this line, either empty or it's a comment;
  }
  find the position of the token that splits option name and its value;
  copy the option name and its value to separate variables;
  removing spaces before and after these variables if necessary;
  if (option == option1) {
     parse value for option 1;
  } else if (option == option2) {
     parse value for option 2;
  } else {
     handle unknown option name;
  }
  check consistency of options if necessary;
}

请注意,部分代码(检查变量周围的空行,注释和空格)是为了在编写配置文件时提供额外的灵活性。例如,您不会因意外地在此处留下额外空间而导致程序崩溃。

这假设您的配置文件如下所示:

# comment for option 1
option1 = value1

# comment for option 2
option2 = value2

...

答案 8 :(得分:2)

答案 9 :(得分:2)

我在C中写了一个干净,无依赖,事件驱动的INI文件解析器。我决定将它扔进public Hg repository。它是MIT许可的,因此您可以随意使用它。

答案 10 :(得分:1)

您是否考虑将配置值存储为环境变量? :) 配置文件将是您在程序之前运行的shell脚本。 (实际上shell脚本会执行它来保存变量)。这样你就可以使用shell作为配置解析器:)

使用http://www.google.com/codesearch和“read config”

请参阅Example for httpd

答案 11 :(得分:0)

C不像Python那样简单,但是我们仍然想要“ import”之类的东西。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*conf*/
/*
 * #ip
 * ip:192.168.0.0.1
 * #port
 * port:8888
 * */
/*read conf*/
void test()
{
        FILE *fp = fopen("conf", "r");
        if (fp == NULL)
        {
                return;
        }

        char line[1024] = { 0 };
        while (!feof(fp))
        {
                memset(line, 0, 1024);
                fgets(line, 1024, fp);
                if (line[0] == '#')
                {
                        continue;
                }

                int len = strlen(line);
                char *pos = strchr(line, ':');
                if (pos == NULL)
                {
                        continue;
                }
                char key[64] = { 0 };
                char val[64] = { 0 };

                int offset = 1;
                if (line[len - 1] == '\n')
                {
                        offset = 2;
                }

                strncpy(key, line, pos - line);
                strncpy(val, pos + 1, line + len - offset - pos);

                printf("%s -> %s\n", key, val);
        }
}

int main()
{
        printf("read start...\n");
        test();
        printf("read success!!!\n");
        int c = getchar();
        return 0;
}

以上基于conf格式,格式为x:y ...

ip:192.168.0.0.1
port:8888