C-ben hogyan parse-olnal fel konfig file-t?

 ( sj | 2009. november 1., vasárnap - 0:31 )

Adott egy konfig file, pl.

var1=value1
var2=value2
...

amit az alabbi struct-ba kellene beletenni:

struct __config cfg {
int var1,
int var2,
char var3[100],
char var4[100],
...
};

Hogyan oldanad meg? Olyasmire gondoltam, hogy egy while ciklusban beolvasom a konfig file-t, es szetszedem a sorokat key-value parokra, ez ok, de hogy lehet elerni a cfg->var1-et, ill. a cfg->var2-t?

Ilyesmire gondoltam (pszeudokoddal):

while((key, val) = read(line)){
if(line == int_valtozo) parser_int_var(cfg->key, val);
if(line == char_valtozo) parse_char_var(cfg->key, val);
}

Azt, hogy melyik valtozo int, ill. char, azt tetszes szerinti modon lehet nyilvantartani, pl. tombben stb.

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

Léteznek kész eszközök az ilyen jellegű feladatokra: lex és yacc (GNU alapokon flex és bison).

http://directory.fsf.org/project/bison/
http://directory.fsf.org/project/flex/

Ezek a programok lehetőséget biztosítanak pl. programozási nyelvi felismerők írására, kimenetük C forráskód.

G.
============================================
"Share what you know. Learn what you don't."

ezek kicsit tullonek egy konfigfile parsolason, nem gondolod?:)

mindjart tanitsuk meg LR parsert irni!

A bison (ami asszem' tartalmaz lexikális elemzőt is, de ez nem biztos) LALR(1)-es elemző :)
Egyébként meg hogy túllő-e, azt neki kell látnia: pl. akar-e bal oldalon szereplő szimbólumot később a jobb oldalon látni, jobb oldalon több szimbólumot... :)

szerk.: de ha csak azt megnézed, h van int_valtozo meg char_valtozo, tehát szét kell szedni típusonként, amihez minimum regexp kell, szerintem csak jól jár vele.

Én bizony BISON -nal csinálnám :)

Nagyban elősegiti a jövőbeli bővithetőséget.

En szepen felparsolnam, eltennem egy lancolt listaba a kulcs - ertek parokat, szovegkent.
Aztan irnek plyan fuggvenyeket, pl. hogy int GetIntValue(key,default). Ez kikeresi a listabol a key nek megfelelo part, aztan az ereteket int-te alakitja, es visszaadja. Ha nem tudja, vagy nincs ilyen key, akkor a default -ot adja vissza.
Ugyanigy megirnam a GetBoolValue(key, default), a GetCharValue(key,default), char* GetStringValue(key,default), stb.stb. fuggvenyeket.
Meg kell majd egy fuggveny, ami felszabaditja a listat.

Minezt meg lehet ugy csinalni, hogy definialsz mondjuk egy ilyemi struct -ot:

struct {
char *key,*value;
} _config_item;

struct {
_config_item *item,*next,*prev;
} _config_list_item;

struct {
char filename[255];
File* file;
_config_list_item * first_item,*last_item;
} ConfigFile;

Aztan kell egy olyan fuggveny, ami felparsolja a konfig-fajlt, legyen mondjuk:

ConfigFile* LoadConfigFile(char* filename);

Ha nem sikerult, null -t ad vissza, ha sikerult.

Aztan kell ami vegig megy a listan, es mindent felszabadit:

void DoneConfig(configFile *)

Es a fuggvenyek, amik kiszedik kulcs alapjan az ertekeket, meg mondjuk igy neznenek ki:

int GetIntValue(ConfigFile*, char* key, int default);

Igy univerzalis kodot keszitenel, annyi config fajlt tudnal egy idoben hasznalni, amennyit akarnal.
Bar biztos van kesz kod erre, de gyakorlakent erdemes megirni, nem nagy ordongosseg.

libconfig?

--
Fontos feladatot soha ne bizz olyan gepre, amit egyedul is fel tudsz emelni!

Én végül írtam egyet magamnak az .ini fájlok mintájára, két rutin hívás a végeredmény:
int FetchCfg(char *FileName,char *Section,char *Entry,char *Frm,...);
int PatchCfg(char *FileName,char *Section,char *Entry,char *Frm,...);
a vfprintf és a vsscanf az alapja a varargs cuccok között nézd.

* Én egy indián vagyok. Minden indián hazudik.

libxml2 parser?

koszonom mindenki hozzaszolasat, meg gondolkodom a dolgon...

SPAMtelenül - MX spamszűrő szolgáltatás, ahogyan még sosem próbálta

Bár C++ és nem C, de remek megoldás: http://code.jellycan.com/simpleini/

Uram eg... ez elso korben strtok masodik korben regex, ennel bonyolultabb egy ilyennek nem kell. A regex meg jo is, mert ha mar ugyis kitanultad a regexet, lehet parsolaskor rogton validalni is a beolvasott erteket.
Mondjuk en egy nev-ertek strukt-ot hasznalnek vagy tombben vagy lancolt listaban.
--

()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

#include 
#include 
#include 
#include 

struct __config {
	int var1;
	int var2;
	char var3[100];
	char var4[100];
} cfg;

int string_parser(char * src, char *target, int limit)
{
	strncpy(target,src,limit);
	target[limit]='\0';
	return 0;
};

int int_parser(char * src, int * target, int limit)
{
 *target=strtol(src, (char **) NULL, 10);
 return 0;
};

struct _parse_rule
{
	char * name;
	int(*parser)(char*,void*,int);
	size_t offset;
	int limit;
};

struct _parse_rule config_parse_rules[]=
{
	{"var1",int_parser,offsetof(struct __config,var1),sizeof(cfg.var1)},
	{"var2",int_parser,offsetof(struct __config,var2),sizeof(cfg.var2)},
	{"var3",string_parser,offsetof(struct __config,var3),sizeof(cfg.var3)},
	{"var4",string_parser,offsetof(struct __config,var4),sizeof(cfg.var4)},
	{NULL,NULL,0,0}
};

int parse(FILE* file, struct __config *target_cfg, struct _parse_rule *rules)
{
	 char *line = NULL;
	 size_t size=0;
	 while (0

Valami ilyesmi jutott eszembe, nemi makrozassal el lehet erni, hogy uj kulcs, vagy tipus hozzadasa csak par sor plusz legyen.


Amit nem lehet megirni assemblyben, azt nem lehet megirni.

ennél egy picit hosszab univrzálisra megírni, olyan módod, ahogy fentebb írtam.

Persze. De a kerdezo egy strukturat akkar kitolteni C-ben viszonylag egyszeruen, velhetoleg egyszer indulaskor.
Java/C++ Qt vilagban teljesen megszokott amit irtal.

A getter sereged, csomo olyan ertek megadasara kenyszerit a getterek hivasakor, amit struktura kezdo erteknek is meg lehetne adni forditas idoben is akkar.

A perser amit javasolsz stringkent tarolna mindent, es lekerdezeskor konvertalna mindig. (Tobblet koddal, memoriaval esetleg proxizva is megoldhato)

(perl -ben, meg vegtelen egyszeru lenne a dolog :) )

Amit meg erdemes megjegyzni kodrol, hogy O(n*m) -en stringet hasonlit ahol m~n. 1000 kulcsnal mar erdemes, int -eknek megfeltetni, vagy fat hasznalni, vagy hash-eket.


Amit nem lehet megirni assemblyben, azt nem lehet megirni.

while (0<getline(&line,&size,file))
 {
	 	char *chpos=strchr(line,'=');
		if (chpos)
		{
			*chpos='\0';
			int i=0;
			while ( rules[i].name)
			{
				
				if (!strcmp(line,rules[i].name))
				{
					 if (rules[i].parser(chpos+1,(char*)target_cfg+rules[i].offset,rules[i].limit))
					 {
					 	printf("Failed to parse %s : \"%s\"",line,chpos+1);
					 }
					 break;
				}				
				i++;
			}
			if (!rules[i].name)
			{
				printf("Unknown key \"%s\" \n",line);
			}
		}
	 }

	 if (line) free(line);
}

int main()
{
	FILE * file;
	file=fopen("test.file","r");
	parse(file,&cfg,config_parse_rules);
	printf("var1: %d \n",cfg.var1);
	printf("var2: %d \n",cfg.var2);
	printf("var3: %s \n",cfg.var3);
	printf("var4: %s \n",cfg.var4);

}

Lemaradt a fele


Amit nem lehet megirni assemblyben, azt nem lehet megirni.

Az include-k is hianyosak, de mindegy. Maskor &lt;, tudod... :-)
--

()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

Kossz, turul, hajszalpontosan ilyenre gondoltam. Kis modositassal be is epitettem a clapf konfig parse-olasaba. Annyit tuningoltam rajta, hogy meg lehet adni default erteket a strukturaban, ill. kezeli a float + multiline stringeket is.

Az offsetof() fv-t eddig nem ismertem, ill. a 'struct _parse_rule' definicioval is tanultam valamit, bar utobbi nelkul is megoldhato lenne a feladat.

Az egyeb lib-ekkel is biztos elegansan meg lehet oldani a feladatot, de (szamomra) annal jobb, minel kevesebb kulso lib kell hozza.

Megegyszer koszonet mindenkinek, aki megosztotta az otletet/kodjat :-)

SPAMtelenül - MX spamszűrő szolgáltatás, ahogyan még sosem próbálta