Обработка файловой конфигурации в C (Unix) - PullRequest
24 голосов
/ 05 января 2009

Это, наверное, одна из самых распространенных задач / проблем при программировании; Вам нужно где-то хранить конфигурацию вашего приложения.

Пока я пытаюсь создать веб-сервер или другие приложения, я бы хотел сохранить код как можно более чистым, поскольку мой основной интерес к программированию - архитектура. В результате я хочу сохранить конфигурации в файле, который можно изменить, не перекомпилируя программное обеспечение.

Я здесь не для того, чтобы заново изобретать колесо или что-то в этом роде, поэтому я хотел бы создать ридер конфигурации в C на * nix. Конфигурация может выглядеть очень похоже на конфигурацию любого другого программного обеспечения; Apache, vsftpd, MySQL и т. Д.

Основной вопрос: как вы читаете из текстового файла и эффективно обрабатываете каждую строку (в чистом C)? Нужно ли использовать fgetc() и обрабатывать каждый символ?

Ответы [ 11 ]

13 голосов
/ 05 января 2009

Хммм LibConfig .

Я нашел это в Wiki

12 голосов
/ 05 января 2009

Разные люди дали достаточно хороший совет - пример Pure-FTP интересен.

Вы также должны прочитать TAOUP (Искусство программирования Unix) E S Raymond. У него есть примеры множества конфигурационных файлов. В нем также изложены канонические идиомы для файлов конфигурации. Например, вам, вероятно, следует разрешить '#' начинать комментарий до конца строки и игнорировать пустые строки. Вам также следует решить, что вы будете делать, если файл конфигурации содержит строку, которую вы не понимаете - игнорировать ли и продолжать молча или жаловаться. (Я предпочитаю вещи, которые жалуются; тогда я знаю, почему то, что я только что добавил, не имеет никакого эффекта - тогда как тихое игнорирование означает, что я не знаю, имеет ли эффект только что добавленная мной запись.)

Другая проблема связана с поиском файла конфигурации. Делаете ли вы это с помощью скомпилированного местоположения, местоположения установки по умолчанию с переменной среды для переопределения или каким-то другим чудом? Убедитесь, что есть опция командной строки, которая позволяет указывать файл конфигурации абсолютно - даже рассмотрите возможность сделать это единственным способом сделать это.

В противном случае, в широких пределах, будьте проще, и все будут счастливы.

9 голосов
/ 05 января 2009

Хорошо, давайте перейдем к другой части. Вы должны думать о том, что вы хотели бы иметь в качестве «языка». В мире UNIX канонической версией, вероятно, является текст с разделителями-пробелами (например, /etc/hosts) или текст с разделителями ":" (например, /etc/passwd).

У вас есть пара опций, в некотором смысле самым простым является использование scanf (3). Снова, прочитайте man-страницу для деталей, но если строковая запись - что-то вроде

port    100

тогда вы будете искать что-то вроде

char inbuf[MAXLINE];
int  val;

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

Вы можете получить немного больше гибкости, если напишите простой анализ FSA: читайте символы по одному из строки и используйте конечный автомат, чтобы определить, что делать.

6 голосов
/ 05 января 2009

Хорошо, вот реальный пример кода 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.
6 голосов
/ 05 января 2009

Есть несколько способов. Вы не должны использовать 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 */
}

Вы также можете посмотреть на libini на Sourceforge.

5 голосов
/ 05 января 2009

Зачем вам когда-либо писать этот код с нуля, это было сделано так много раз; просто найдите хорошую реализацию F / OSS и используйте ее.

Как вы читаете из текстового файла и эффективно обрабатываете каждую строку

Не беспокойтесь об эффективности, это не имеет значения для чтения файлов конфигурации. Вместо этого используйте простоту и удобство обслуживания.

3 голосов
/ 05 января 2009

Как я бы это сделал (псевдокод):

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

...
3 голосов
/ 05 января 2009

Еще одно решение - Pure-ftpd

В отличие от многих демонов, Pure-FTPd не читает ни один файл конфигурации. Вместо этого он использует параметры командной строки. ... Добавление парсера для файлов конфигурации на сервере - это плохо идея. Он замедляет все и не нуждается ни в каких ресурсах.

А для опций есть getopt

2 голосов
/ 06 января 2009

Я недавно написал чистый синтаксический анализатор INI-файлов, управляемый событиями, в Си. Я решил добавить его в публичный репозиторий Hg . Он имеет лицензию MIT, поэтому вы можете использовать его в любом месте.

2 голосов
/ 05 января 2009

В GNU LibC

есть также функция getline () и друзья .
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...