PDA

View Full Version : [C] Problema ciclo for dentro un ciclo while


Y3PP4
10-08-2009, 21:34
Giorno,

sto facendo delle prove scrivendo un piccolo parser (niente di chè, solo escape di stringhe).
Attualmente ho messo a punto un algoritmo semplice semplice che prevede tramite un
while(!feof( fp )) la chiamata a varie funzioni, una delle quali è fgets.
Ottenuta la stringa poi passo tutto a un'altra funzione che tramite un ciclo for così definito:
for( i=0; i<strlen( line ); i++ )
richiama continuamente uno switch(line[i]) e in seguito testa: se il carattere non è una tabulazione o spazio lo salva in un'altro puntatore e al termine del ciclo for ritorna la stringa.

Qui sorge il problema. Se provo a stampare le varie stringhe ottengo tutto con successo, ma ahimè l'ultima linea mi viene stampata due volte.

Qualcuno sà da cosa possa dipendere il problema?

Di seguito posto il problema semplificato (per non complicare le cose):

// importo gli header necessari
void cleanString( char *line, char *escapedLine );
int main()
{
// ... fp è argv[1]
char line[500];
char *escapedLine;
while( !feof( fp ) )
{
fgets( line, sizeof( line ), fp );
// alloco la memoria
escapedLine = malloc ( strlen( line ) + 1 ) * ( sizeof( char ) ) );
cleanString( line, escapedLine ); //
printf("%s\n", escapedLine);
free( escapedLine );
}
return( EXIT_SUCCESS );
}
void
cleanString( char *line, char *escapedLine )
{
unsigned int i;
unsigned int a = 0; // counter per cleanLine
for( i=0; i<strlen( line ); i++ )
{
switch( line[i] )
{
case ' ' :
case '\t':
case '\n':
break;
default:
cleanLine[a] = line[i];
a++;
break;
}
}
return;
}


Questo è quanto.
Supponendo di avere un file con semplici frasi come potrebbe essere:
Giorno a tutti,
oggi mi sento
bene.

Ottengo come risultato:
Giornoatutti,
oggimisento
bene.
bene. <-- ripetuto due volte!

Grazie a tutti, buona notte.

DanieleC88
10-08-2009, 22:54
Imposta il primo carattere della stringa a '\0' prima di leggere con fgets(), visto che è un buffer statico, dovrebbe bastare.

ciao ;)

Y3PP4
11-08-2009, 00:15
Grazie mille per la risposta :)

Il sistema funziona (mi stampa solo un'ultima riga bianca ma dato che non dovrò stamparlo ed è un carattere nullo non dovrei avere problemi col parser...)

Come mai bisogna usare questo escape (o hack che dir si voglia) ?
Io avevo esplicitamente detto:
"mentre non incontri la fine del file -> leggi una riga -> leggi ogni carattere della riga -> se non è uno spazio mettilo da parte -> ritornami tutti i caratteri messi da parte -> stampa i caratteri ritornati"

Esegue palesemente un ciclo in più (in cui mi pare di capire che la stringa rimane inalterata) di quello richiesto...
inoltre perchè la stringa rimane immutata? non è dichiarata come static...

chiedo scusa ma alla solita se è possibile vorrei chiarire il concetto :)

Buona notte e mille grazie ancora!

nuovoUtente86
11-08-2009, 00:48
effettua una prima fgets fuori dal while e posiziona la chiamata come ultima istruzione del while in modo da sincronizzare con feof la posizione sullo stream.

DanieleC88
11-08-2009, 07:12
Esegue palesemente un ciclo in più (in cui mi pare di capire che la stringa rimane inalterata) di quello richiesto...
inoltre perchè la stringa rimane immutata? non è dichiarata come static...
Perché finché non "fallisce" per la prima volta la fgets() non ti viene dato l'EOF, quindi l'ultimo ciclo sarà quello in cui la fgets() non avrà più niente da leggere. Il problema era che il tuo buffer era un'array dichiarata come variabile locale, che nasce e muore con la funzione in cui è stata dichiarata: al termine di ogni ciclo, i vecchi contenuti non vengono cancellati (al limite devi terminare tu a mano la stringa, come ti ho consigliato di fare io), e quindi nell'ultimo ciclo prima dell'EOF ottieni comunque la riga che già era dentro il buffer.
Alternativamente, puoi fare come consiglia nuovoUtente86 ed ottenere lo stesso effetto.

ciao ;)

Y3PP4
11-08-2009, 21:26
Anzitutto mille grazie ad entrambi per la risposta.
Mille grazie nuovoUtente86 per il suggerimento, adesso che me lo hai esposto, tra l'altro, sembra la soluzione ad hoc al problema, mi sarà molto utile : grazie mille!

Perché finché non "fallisce" per la prima volta la fgets() non ti viene dato l'EOF, quindi l'ultimo ciclo sarà quello in cui la fgets() non avrà più niente da leggere. Il problema era che il tuo buffer era un'array dichiarata come variabile locale, che nasce e muore con la funzione in cui è stata dichiarata: al termine di ogni ciclo, i vecchi contenuti non vengono cancellati (al limite devi terminare tu a mano la stringa, come ti ho consigliato di fare io), e quindi nell'ultimo ciclo prima dell'EOF ottieni comunque la riga che già era dentro il buffer.
ciao ;)

Ancora una volta mi hai illuminato con una esposizione chiarissima: ti sono (nuovamente) debitore! Adesso mi è tutto chiaro;
soprattutto perchè dare il fine stringa al primo carattere prima dello fgets.
Grazie mille (inoltre mi sono chiarito anche il fatto che feof "cattura" un eof settato da un'altra funzione e non lo setta da solo).

Buona serata a tutti :-).

Y3PP4
11-08-2009, 22:07
Uh, un'ultima cosa che mi era sfuggita.
Io dealloco la memoria con free(fp)... se libero la memoria contenente la stringa come fà ancora a restare in memoria nel ciclo successivo?

Solo una piccola curiosità

:)

Buona serata,
ciao.

DanieleC88
11-08-2009, 22:46
Perché la prendi da line e ce la ricopi dentro (la fgets() opera sulla line allocata staticamente, non sulla escapedString allocata dinamicamente). :D

ciao ;)

Y3PP4
11-08-2009, 23:00
Perché la prendi da line e ce la ricopi dentro (la fgets() opera sulla line allocata staticamente, non sulla escapedString allocata dinamicamente). :D

ciao ;)

:rolleyes: ....

non ho parole: se mi scordo pure come funzionano gli algoritmi più semplici che scrivo pochi giorni prima, sarà meglio che ricorra ad una cura di fosforo.
Il fatto (a mia giustificazione :D ) è che nel programma uso un puntatore ad una sequenza di comandi (dichiarati come puntatori a funzione) per usare anzichè ad esempio fgets(...);
parser->getLine(...);

Beh che dire, dopo questa "svista" sarà meglio che con il prossimo post possa recuperare credito nel forum :p, magari chiedendo(ti) consiglio (o aiuto) su un algoritmo, quantomeno brillante.

Buona notte :). (a mia giustificazione posso chiamare anche in causa l'ora? e la giornata in piscina?)

DanieleC88
11-08-2009, 23:07
Lascia stare, non servono giustificazioni... c'è chi (io per primo) ha fatto e fa cose ben peggiori. :D

ciao ;)