Indice

Creare un semplice parser per file configurazione

Autore: Fabio Di Matteo
Ultima revisione: 01/07/2008

Spesso i software utilizzano file di testo per memorizzare le impostazioni e le preferenze.
Questi file di solito hanno una struttura simile:

#I commenti iniziano per # e possono servire per disabilitare una chiave

key0 zero
key1 uno
#key2 due
#Questo è soltanto un commento
key3 tre
key4 quattro
key5 cinque 

dove keyn è il nome del valore, invece zero, uno,due … il valore. Per ineragire con questi file sono necessarie funzioni come le seguenti:

char* keyread(char *path, char* key);
void keywrite(char *path, char* key, char* value);
int keydel(char *path, char* key);

Il file keys.c

Ci serviranno 3 funzioni, una per leggere le chiavi, una per scriverle e l'altra per cancellarle.

#include <string.h>
#define MAXKEY  50
#define MAXVALUE  50
#define MAXLINE 256
 
int wordcount(char* line);
char* keyread(char *path, char* key);
void keywrite(char *path, char* key, char* value);
int keydel(char *path, char* key);
 
 
 
 
char* keyread(char *path, char* key){
	FILE *f=fopen(path, "r");
	char valuetmp[MAXVALUE], line[MAXVALUE+MAXKEY], *value, key_now[MAXKEY] ;
	int i;
 
	while (feof(f)==0){
		fgets(line,MAXVALUE+MAXKEY,f);
		if (line[0]!='#' && wordcount(line)==2){
			sscanf(line, "%s %s\n",key_now, valuetmp);
			if (strcmp(key_now, key)==0){
				value=malloc(sizeof(valuetmp));
				strcpy(value,valuetmp);
				return value;
			}
		}
	}
	fclose(f);
}
 
void keywrite(char *path, char* key, char* value){ 
	FILE *f=fopen(path, "r");
	int i, pos=0, idxline=0 ;
	char valuetmp[MAXVALUE], line[MAXVALUE+MAXKEY], *nline[MAXVALUE+MAXKEY], key_now[MAXKEY] ;
 
	//get key's position
	while (feof(f)==0){
	pos++;
		nline[pos-1]=malloc(MAXLINE);
 
		fgets(nline[pos-1],MAXLINE,f);
		if (nline[pos-1][0]!='#' && wordcount(nline[pos-1])==2){
			sscanf(nline[pos-1], "%s %s\n",key_now, valuetmp);
			if (strcmp(key_now, key)==0 && idxline==0){
				idxline=pos-1;
			}
		}
	}
	fclose(f);
 
	//edit or append key
	nline[idxline]=malloc(sizeof(key)+sizeof(value)+1);
	if (idxline!=0){
		//edit key
		sprintf(nline[idxline] ,"%s %s\n",key,value);
		FILE *f=fopen(path, "w");
 
		for (i=0;i<=pos-1;i++)	fputs(nline[i],f);
		fclose(f);
 
	}else{
		//append new key
		FILE *f=fopen(path, "a");
		sprintf(nline[pos-1] ,"%s %s\n",key,value);;
		fputs(nline[pos-1],f);
		fclose(f);	
	}
 
}
 
 
 
int keydel(char *path, char* key){ 
	FILE *f=fopen(path, "r");
	int i, pos=0, idxline=0 ;
	char valuetmp[MAXVALUE], line[MAXVALUE+MAXKEY], *nline[MAXVALUE+MAXKEY], key_now[MAXKEY] ;
 
	//get key's position
	while (feof(f)==0){
	pos++;
		nline[pos-1]=malloc(MAXLINE);
 
		fgets(nline[pos-1],MAXLINE,f);
		if (nline[pos-1][0]!='#' && wordcount(nline[pos-1])==2){
			sscanf(nline[pos-1], "%s %s\n",key_now, valuetmp);
			if (strcmp(key_now, key)==0 && idxline==0){
				idxline=pos-1;
			}
		}
	}
	fclose(f);
 
	//delete key
	nline[idxline]=malloc(sizeof(key)+MAXVALUE+1);
	if (idxline!=0){
		FILE *f=fopen(path, "w");
		for (i=0;i<=pos-1;i++){
			if (i!=idxline)fputs(nline[i],f);
		}
		fclose(f);
 
	}else{
		return -1;
	}
}
 
 
 
 
int wordcount(char* line){
	int i,count=0;
	for(i=0; i<strlen(line);i++){
		if (isspace(line[i]) && !isspace(line[i+1]) ) count++;	
	}
	return count;
}

La main()

Ecco una tipica main() che potrebbe sfruttare le funzioni.

L'eseguibile si userà in questo modo:

keys -r [chiave] per leggere il valore della chiave;
keys -w [chiave] [valore] per assegnare un valore ad una chiave (se la chiave non esiste la crea) ;
keys -d [chiave] per eliminare la chiave;

#include <stdio.h>
#include "keys.c"
 
#define PREF "preference.conf"
 
 
 
 
int main(int *argc, char *argv[]){
 
	if (argc>2){ 
		if (argv[1][1]=='r'){
			char *valore=keyread(PREF , argv[2]); 
			printf("%s\n",(valore!=NULL ? valore:"0")) ;
		}
		if (argv[1][1]=='w'){
			keywrite(PREF,argv[2], argv[3] );
		}
		if (argv[1][1]=='d'){
			if (keydel(PREF,argv[2])==-1) printf("key not present!\n") ;
		}
 
	}else{
		printf("USAGE: keys -r or -w arg1 [arg2]\n  ");
	}
return 0;
}

Commentiamo la funzione keyread

Commentiamo soltanto la funzione keyread in quanto le altre funzioni sono un surrogato della stessa, soltato che scrivono invece che leggere dal file.
La funzione keyread restituisce un puntatore a char contenente il testo del valore che vogliamo prendere oppure NULL se non trova la chiave o la chiave è scritta in un formato sbagliato.
Ecco il codice di keyread commentato:

char* keyread(char *path, char* key){
	FILE *f=fopen(path, "r");
	char valuetmp[MAXVALUE], line[MAXVALUE+MAXKEY], *value, key_now[MAXKEY] ;
	int i;
 
	while (feof(f)==0){ /
		fgets(line,MAXVALUE+MAXKEY,f);//finchè non finisce il file metti ogni riga in array
/*se nella riga corrente non ci sono commenti e se nella riga ci sono 2 parole soltanto (chiave +valore)*/
		if (line[0]!='#' && wordcount(line)==2){ 
                        /*metti il nome della chiave in key_now e il valore in valuetmp*/
			sscanf(line, "%s %s\n",key_now, valuetmp);
                 /*se key_now corrisponde alla chiave che cerchiamo ritorna il valore della chiave*/
			if (strcmp(key_now, key)==0){
				value=malloc(sizeof(valuetmp));
				strcpy(value,valuetmp);
				return value;
			}
		}
	}
	fclose(f);
}