View Full Version : [C] feof() nella guardia del while!
Allora... Io per un progetto devo creare una rubrica tramite l'uso delle Hash Table... tutto perfetto, se non per un piccolo problema...
ho gestito tutti gli errori ed il programma è a prova di niubbo ma che niubbo non si può!! ora spiego meglio:
la rubrica deve appoggiarsi ad un file, prelevare le informazioni dal file e, dare l'opportunità all'utente di, salvarle sempre sul file... bene ok... ora il punto è che devo prelevare i dati dal file ed inserirli nella Hash Table (costruita come array di strutture record)... e per fare questo ho usato un while e nella guardia ho usato la condizione !feof(file)
ora il punto qual'è... il punto è che se il file è completamente vuoto, allora mi viene restituito un errore nella funzione substring(char *, int) che è quella funzione che mi permette di estrapolare, riga per riga, i dati dal file... questo significa che il ciclo while viene eseguito almeno una volta!
ma io mi chiedo... com'è possibile che il ciclo venga eseguito comunque almeno una volta?! come posso ovviare a questo problema? davvero, ho il progetto pronto, la relazione idem...programma a prova di utente alla "Mr. Been" eppure non posso presentare il progetto solo perché se il file è vuoto, restituisce un errore nella funziona substring(char *, int), come è giusto che sia, perché il file è vuoto!
vi ringrazio per eventuali suggerimenti... non so dove sbatterci la testa...
Questo è il codice della funzione t_build() che ha il compito di prelevare i dati da file ed inserire in hash table...
void t_build()
{
char temp[(MAX_LENGTH * 2) + 1];
char *name, *email;
unsigned int entry, collision = 0;
while(!feof(file))
{
// leggo una riga dal file
fgets(temp, (MAX_LENGTH * 2) + 1, file);
// estrapolo le informazioni dalla riga letta e le assegno a name ed email
name = substring(temp, -1);
email = substring(temp, (strlen(substring(temp, -1)) + 1));
/* popolo la tabella:
Fase 1) calcolo l'entry */
entry = linear(hashing(name), 0, name);
if(entry != hashing(name))
{
collision = 1;
} else collision = 0;
/* Fase 2) popolo fisicamente, con name ed email, il record ad indice entry
nella hash table */
ht[entry].key = name;
ht[entry].value = email;
ht[entry].collision = collision;
count = counter(count, 0);
}
ht[entry].key = NULL;
ht[entry].value = NULL;
ht[entry].collision = 0;
count = counter(count, 1);
}
apocalypsestorm
29-01-2009, 00:09
sicuro di aver aperto correttamente il file ?
bisognerebbe prima controllare il valore della variabile "file==NULL"
http://www.cplusplus.com/reference/clibrary/cstdio/feof.html
Io faccio sempre così:
while(1)
{
// leggo una riga dal file
fgets(temp, (MAX_LENGTH * 2) + 1, file);
if(!feof(file)
break;
// estrapolo le informazioni dalla riga letta e le assegno a name ed email
name = substring(temp, -1);
email = substring(temp, (strlen(substring(temp, -1)) + 1));
/* popolo la tabella:
Fase 1) calcolo l'entry */
entry = linear(hashing(name), 0, name);
if(entry != hashing(name))
{
collision = 1;
} else collision = 0;
/* Fase 2) popolo fisicamente, con name ed email, il record ad indice entry
nella hash table */
ht[entry].key = name;
ht[entry].value = email;
ht[entry].collision = collision;
count = counter(count, 0);
}
Oppure in questo caso, visto che fai una sola lettura:
while(fgets(temp, (MAX_LENGTH * 2) + 1, file)
{
...
}
@ apocalypsestorm allora la rubrica funziona perfettamente... cioè il file lo apre bene... l'unica pecca della rubrica (che mi allontana da una possibile lode) è proprio il fatto che se il file è vuoto, allora mi dà Access Violation (nulla da dire, se il file viene iterato cmq... per come è definita substring(char *, int)... il problema è che non dovrebbe essere iterato in quel caso dato che da quel che ho capito !feof(FILE *) dovrebbe uscire dal ciclo nel momento in cui il puntatore all'interno del file è NULL)...
l'unico problema è quello... certo, l'alternativa è presentare la rubrica con almeno un contatto... ma è poco elegante... XD
@ cionci ora provo a vedere e ti so dire... perché non so dove sbatterci la testa... :muro: :muro:
Io faccio sempre così:
while(1)
{
// leggo una riga dal file
fgets(temp, (MAX_LENGTH * 2) + 1, file);
if(!feof(file)
break;
// estrapolo le informazioni dalla riga letta e le assegno a name ed email
name = substring(temp, -1);
email = substring(temp, (strlen(substring(temp, -1)) + 1));
/* popolo la tabella:
Fase 1) calcolo l'entry */
entry = linear(hashing(name), 0, name);
if(entry != hashing(name))
{
collision = 1;
} else collision = 0;
/* Fase 2) popolo fisicamente, con name ed email, il record ad indice entry
nella hash table */
ht[entry].key = name;
ht[entry].value = email;
ht[entry].collision = collision;
count = counter(count, 0);
}
Oppure in questo caso, visto che fai una sola lettura:
while(fgets(temp, (MAX_LENGTH * 2) + 1, file)
{
...
}
Il secondo mi ha illuminato... non pensavo fosse possibile inserire la funzione fgets(char *, int, FILE *) nella guardia del while... XD funziona perfettamente... inoltre mi hai risolto anche un'altro problema (che si notava dal codice da me proposto... ovvero che l'ultimo contatto nel file veniva inserito due volte... problema che avevo risolto, cancellando l'ultimo contatto inserito in rubrica (in quanto doppione del penultimo)... è come se mi leggeva due volte l'ultima riga!! in questo modo si è risolto anche quel problema...
grazie mille!!!! davvero molto gentile...
dite che 638 righe di codice, per una rubrica, siano troppe?! XD (preciso che la metà delle righe sono commenti, mentre la metà del codice (tolti i commenti) sono tutte le gestioni degli errori... non avrei mai immaginato che un utente niubbo potesse fare così tanti danni, se non gestiti XD)
Fammi vedere qualche riga della gestione degli errori.
Fammi vedere qualche riga della gestione degli errori.
Ricerca di un elemento... record ht[DIM_TABLE]; è la mia tabella di hash... ovviamente, gli elementi possono avere un entry compreso fra 0 e 42... quindi nel caso in cui l'entry è settato su DIM_TABLE significa che non è un indice valido: errore = contatto non trovato.
//#define DIM_TABLE 43
entry = t_search(name); // t_search(char *) è unsigned int
if(entry == DIM_TABLE)
{ // gestiamo la situazione in cui il contatto cercato non è in rubrica
system(CLEAN);
printf("\n ATTENZIONE! Il contatto cercato non è stato trovato.");
printf("\n\n Premere INVIO per tornare al menu iniziale... ");
}
else
{ /* altrimenti possiamo procedere allo stampo in stdout dell'email
del contatto cercato */
printf("\n Indirizzo e-mail:");
printf("\n >> %s", ht[entry].value);
printf("\n\n Premere INVIO per tornare al menu iniziale... ");
}
Aggiunta di un nuovo elemento... per come sono definite le tabelle di hash, non è possibile aggiungere chiavi identiche (altrimenti nella ricerca, verrebbe trovata solo la prima che c'è nel cluster)... quindi devo impedire che l'utente inserisca due volte la stessa chiave... quindi se t_search(char *) dà un entry valido, allora significa che l'elemento è già stato trovato dentro la tabella di hash e dunque non è possibile aggiungerlo... alternativamente, se count = DIM_TABLE (count è un contatore a livello globale che tiene il conto di quanti spazi sono stati occupati in tabella) allora significa che lo spazio è esaurito...
entry = t_search(name);
if(entry != DIM_TABLE)
{ /* gestiamo la situazione in cui un contatto con lo stesso nome sia
già presente in rubrica */
system(CLEAN);
printf("\n ATTENZIONE! Nome già esistente.");
printf("\n Cancellarlo e ripetere l'operazione.");
printf("\n\n Premere INVIO per tornare al menu iniziale... ");
while(getchar() != '\n');
}
else if(count == DIM_TABLE)
{ /* gestiamo la situazione in cui la tabella è piena e non permette
nuovi inserimenti */
system(CLEAN);
printf("\n ATTENZIONE! Spazio in rubrica esaurito.");
printf("\n Cancellare un contatto per fare un ulteriore inserimento.");
printf("\n\n Premere INVIO per tornare al menu iniziale... ");
while(getchar() != '\n');
}
quelli sono i più semplici ed intuitivi... però c'è anche tutta la gestione di eventuali violazioni in memoria nella funzione di hash come ad esempio:
/*
con j settato su DIM_TABLE;
streq(char *, char *); una mia funzione per confrontare che due stringhe siano esattamente uguali!
*/
while(ht[entry].key != NULL && streq(ht[entry].key, key) && j-- > -1)
{ // ht[entry].key != NULL è necessario per evitare violazioni di memoria
entry = (entry + 1) % DIM_TABLE;
}
if(j < 0 || ht[entry].key == NULL)
{
entry = DIM_TABLE;
}
Poi ho limitato l'utente a non inserire il '-' (trattino) come carattere... dato che nel file i contatti sono scritti in questo formato:
nome 1-email@1
nome 2-email@2
...
nome n-email@n
quindi per prelevare l'email, devo prendere tutto il contenuto fra il '-' (trattino) ed il '\n' (newline)... quindi per ovvie ragioni ho dovuto impedire all'utente che metta il trattino... ed ho messo un controllo sul nome, che controlla se ci sono evenutali '-' (trattini) ed in tal caso li sostituisco con ' ' (spazi)... questo vale sia in inserimento che in ricerca, così ' ' e '-' sono considerati sempre come ' '... poi, appena consegno la rubrica, magari vi fo vedere il progetto in tutte e le sue 658 (ho ottimizzato alcune cose, quindi son aumentate) righe di codice! :D :D
Puoi gestire tutto tramite una funzione:
void print_error(char * what, char * solution)
{
system(CLEAN);
printf("\n ATTENZIONE! %s", what);
printf("\n %s.", solution);
printf("\n\n Premere INVIO per tornare al menu iniziale... ");
while(getchar() != '\n');
}
Puoi gestire tutto tramite una funzione:
void print_error(char * what, char * solution)
{
system(CLEAN);
printf("\n ATTENZIONE! %s", what);
printf("\n %s.", solution);
printf("\n\n Premere INVIO per tornare al menu iniziale... ");
while(getchar() != '\n');
}
ci avevo seriamente pensato.... ma, per rendere il codice leggibile, dovrei istanziare in memoria una nuova variabile char *solution nell'header() (funzione contenente il menu iniziale) che memorizza una stringa a seconda del problema riscontrato...
e sebbene di poco, questo va a pesare in memoria... se posso evitare di usare troppe variabili è meglio... effettivamente stavo pensando di togliere la variabile entry ed usare al suo posto la variabile ch (che memorizza la scelta nel menu iniziale) entrambe sono unsigned int... tanto usata una volta, non serve più, fino a quando non si ritorna al menu iniziale...
Hai problemi di memoria ???
Comunque non capisco perché ti devi complicare la vita memorizzandoti l'errore.
entry = t_search(name);
if(entry != DIM_TABLE)
{
print_error("Nome già esistente", "Cancellarlo e ripetere l'operazione");
}
else if(count == DIM_TABLE)
{
print_error("Spazio in rubrica esaurito", "Cancellare un contatto per fare un ulteriore inserimento");
}
Hai problemi di memoria ???
Comunque non capisco perché ti devi complicare la vita memorizzandoti l'errore.
entry = t_search(name);
if(entry != DIM_TABLE)
{
print_error("Nome già esistente", "Cancellarlo e ripetere l'operazione");
}
else if(count == DIM_TABLE)
{
print_error("Spazio in rubrica esaurito", "Cancellare un contatto per fare un ulteriore inserimento");
}
beh se passo tutto ad un'altra funzione, mi conviene memorizzare la stringa così da compattare il tutto... altrimenti mi conviene lasciare così... cmq ci stavo seriamente pensando anche io... vorrei prima parlare con la docente e vedere il suo punto di vista e quanto ci tiene alla massima ottimizzazione della memoria... e poi scelgo il da farsi... attualmente la rubrica è finita... mancano solo le ultime ottimizzazioni come questa e poche altre (tipo suddividere meglio le funzioni e renderle più leggibili staccando le righe di codice)!
Imho non ha senso pensare all'ottimizzazione della memoria, ma questo per un'altra questione. Mi è parso di capire che stai facendo un qualche algoritmo di hashing. La gestione degli errori devi farla esterna....mi spiego, io, utilizzatore della tua libreria che fa l'hashing non mi posso permettere che tu mi stampi qualcosa sullo standard output.
Quindi la gestione e stampa degli errori la devi fare dall'esterno.
Ad esempio ritornando un un intero con il tipo dell'errore e/o settando una variabile statica con l'ultimo errore avvenuto.
Sarà l'utente che si dovrà occupare, in caso di errore, di passare una stringa ad un funzione della libreria che verrà riempita con il testo dell'errore.
Una libreria non si dovrà mai occupare di gestire los tandard input e lo standard output.
Se questa gestione degli errori è al di fuori della libreria, allora non ti dovrai preoccupare di risparmiare la memoria.
Imho non ha senso pensare all'ottimizzazione della memoria, ma questo per un'altra questione. Mi è parso di capire che stai facendo un qualche algoritmo di hashing. La gestione degli errori devi farla esterna....mi spiego, io, utilizzatore della tua libreria che fa l'hashing non mi posso permettere che tu mi stampi qualcosa sullo standard output.
Quindi la gestione e stampa degli errori la devi fare dall'esterno.
Ad esempio ritornando un un intero con il tipo dell'errore e/o settando una variabile statica con l'ultimo errore avvenuto.
Sarà l'utente che si dovrà occupare, in caso di errore, di passare una stringa ad un funzione della libreria che verrà riempita con il testo dell'errore.
Una libreria non si dovrà mai occupare di gestire los tandard input e lo standard output.
Se questa gestione degli errori è al di fuori della libreria, allora non ti dovrai preoccupare di risparmiare la memoria.
ah no forse non hai capito XD... la libreria di hash è pulita... restituisce solo valori (validi in caso che l'algoritmo abbia successo... DIM_TABLE (non valido) se l'algoritmo ha avanzato un errore che dovrà essere gestito)
come venga gestito, dipende da chi usa la libreria... infatti la gestione degli errori (a parte la gestione del caso in cui in lettura/scrittura si incontri una key NULL (nella guardia del while)) il resto è tutta fatta fuori dalla libreria hash.h... ma nel file rubrica.c dove effettivamente implemento le operazioni nella rubrica... in hash.h solo ho creato la hash table e la funzione di hash e la gestione delle collisioni con il linear probing...
l'ottimizzazione che stavo pensando (e che pensavo intendessi anche te) è a livello di progettazione software non di libreria hash.h...
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.