View Full Version : [C] Leggere un intero rigo da file
leadergl
13-01-2006, 08:11
Raga ho scritto questo codice in C per leggere un intero rigo da un file di testo senza sapere a prescindere quanto è lungo....ma ci deve essere qualche errore:
int freadline(char **buffer, FILE *stream)
{
// variabili d'appoggio
int indice; // serve per salvare il numero di caratteri letti
char appoggio; // serve a memorizzare il singolo carattere letto
char *temp=(char *)malloc(sizeof(char)); // conterrà tutta la riga
// se è stato realmente aperto un file allora...
if (stream!=NULL)
{
// inizializzo il contatore dei caratteri
indice=0;
// leggo un carattere e lo memorizzo in appoggio
appoggio=fgetc(stream);
// finche il carattere letto non è quello di fine riga o di fine file
while ((appoggio!=EOF) && (appoggio!='\n'))
{
// memorizzo il carattere in temp
temp[indice]=appoggio;
// realloco memoria per temp
realloc(temp,sizeof(char [1]));
// incremento indicie
indice++;
// legggo un carattere e lo memorizzo in appoggio
appoggio=fgetc(stream);
}
temp[indice]='\0';
} else return 0;
*buffer=temp;
return indice;
}
e va usato in questo modo:
int main()
{
char *dest=NULL;
int caratteriletti;
FILE *file;
file=fopen("infile.txt","r");
if (file==NULL)
printf("Errore duranre l'apertura del file!");
else
{
// Legge il primo rigo del file
caratteriletti=freadline(&dest, file);
// Altro codice...
}
system("PAUSE");
return 0;
}
ma c'è qualcosa di malvagio nel codice perchè messo così tutto funziona perfettamente ma se provo a leggere due righe e quindi nel corpo del main aggiungo un'altra:
caratteriletti=freadline(&dest, file);
si verificano delle anomalie....
dove sbaglio?
anomalie di che tipo?
facendo così se richiami la funzione nel main ti leggerà sempre la prima riga
leadergl
13-01-2006, 08:41
beh veramente mi legge anche le altre, il problema è che ci deve essere qualche errore poichè se richiamo la funzione una sola volta il
system("PAUSE");
che c'è nel main viene processato correttamente e mi mostra a schermo quello che ha letto, mentre se richiamo la funzione più volte il programma si apre e si chiude rapidamente senza farmi leggere nulla (riesco a vedere l'output solo se eseguo l'eseguibile dal Prompt del DOS)....
Ciao, ho dato una occhiata al tuo codice (non l'ho provato realmente). Ci sono alcune cose che non vanno:
Quando fai:
realloc(temp,sizeof(char [1]));
Riallochi sempre 1 byte e basta, cioè non "allunghi" il buffer.
Inoltre la realloc ti ritorna un puntatore (void*) perché è possibile che la nuova area sia "spostata".
Comunque leggere 1 carattere per volta e riallocare il buffer ad ogni nuovo carattere non è, in effetti, molto efficiente.
leadergl
13-01-2006, 11:29
me lo sapresti sistemare?
il fatto è che io ho sempre programmato in TurboPascal e da un paio d'anni in VB6 e col C non ho per niente dimestichezza.....help me :p
======== EDIT ==========
l'ho modificato così e funziona...ma è sicuramente migliorabile...help:
int freadline(char **buffer, FILE *stream)
{
// variabili d'appoggio
int indice; // serve per salvare il numero di caratteri letti
char appoggio; // serve a memorizzare il singolo carattere letto
char *temp=(char *)malloc(sizeof(char[10])); // conterrà tutta la riga
// se è stato realmente aperto un file allora...
if (stream!=NULL)
{
// inizializzo il contatore dei caratteri
indice=0;
// leggo un carattere e lo memorizzo in appoggio
appoggio=fgetc(stream);
// finche il carattere letto non è quello di fine riga o di fine file
while ((appoggio!=EOF) && (appoggio!='\n'))
{
// realloco memoria per temp
if (indice>=strlen(temp)) realloc(temp,sizeof(char[indice+10]));
// memorizzo il carattere in temp
temp[indice]=appoggio;
// incremento indicie
indice++;
// legggo un carattere e lo memorizzo in appoggio
appoggio=fgetc(stream);
}
temp[indice]='\0';
} else return 0;
*buffer=temp;
//free(temp);
return indice;
}
Ciao, come al solito posso postare la "mia" soluzione personale. ;)
Se si vuole usare la fgetc (lettura di un singolo carattere per volta):
#define READLINE_EXTEND 128
char *readline (FILE *f, int *plen)
{
char *row = NULL, *row_t = NULL;
int ch, rowlen = 0, buflen = 0;
while ((ch = fgetc (f)) != EOF)
{
if (rowlen >= buflen)
{
buflen = rowlen+READLINE_EXTEND;
row_t = (char *) realloc (row, buflen);
if (row_t == NULL)
break;
}
row = row_t;
if (ch != '\n')
row[rowlen++] = (char) ch;
else
break;
}
if (row_t != NULL)
{
row[rowlen] = '\0';
if (plen != NULL)
*plen = rowlen;
}
else
{
free (row);
row = NULL;
}
return row;
}
Se si vuole usare la fgets (lettura di più caratteri per volta):
#define READLINE_EXTEND 128
char *readline (FILE *f, int *plen)
{
char *row = NULL, *row_t, *p;
int len = 0, pos = 0, rowlen;
do {
len += READLINE_EXTEND;
if ((row_t = (char *) realloc (row, len)) != NULL)
{
row = row_t;
if (fgets (&row[pos], READLINE_EXTEND, f) != NULL)
{
if ((p = strchr (&row[pos], '\n')) != NULL)
{
*p = '\0';
rowlen = p - row;
break;
}
else
pos += READLINE_EXTEND-1;
}
else
row_t = NULL;
}
} while (row_t != NULL);
if (row_t != NULL)
{
if (plen != NULL)
*plen = rowlen;
}
else
{
free (row);
row = NULL;
}
return row;
}
Entrambe "estendono" il buffer di READLINE_EXTEND caratteri ogni volta che serve nuovo spazio.
Entrambe ritornano NULL se si raggiunge EOF o se la realloc fallisce.
leader,
forse non ti piacera' detto da un "novellino" ma il tuo metoto mi sembra molto complicato e poco efficiente.
Personalmente preferisco fissare la massima riga che accetto di leggere e allocare memoria di conseguenza. Questo dei vantaggi:
1. Se la riga che arriva e' maggiore da quella che ho fissato, probabilmente mi trovo davanti ad un file corrotto oppure davanti ad un attacco hacker, quindi e' meglio non leggere tutto e caricarlo in memoria.
2. Non si puo' andare ad allocare memoria solo perche' la riga e' lunga... la gestione degli overflow e' piu' complicata che nel caso di dimensioni massime fisse.
3. Riallocare ogni volta 1 carattere comporta una penalita' enorme a livello di heap, e puo' provocare una frammentazione terribile. In piu' a seconda dell'implementazione potrebbe comportare un copy ad ogni rialloco di carattere.
Se pero' vuoi proprio leggere tutta la riga, suggerirei di leggere dal file a blocchi (e' molto piu' efficiente, anche se comporta uno spreco medio di memoria di blocksize/2). Pero' hai il vantaggio di non frammentare il heap.
Ad occhio e croce l'algo che scriverei potrebbe essere cosi (e occhio che preserva il \n finale):
#define BLOCK_SIZE 1024
char *read_long_line( FILE *f ) {
int curread ; // caratteri letti nel blocco
int totread ; // totale caratteri di tutti i blocchi
char *s ; // stringa totale.
char *p ; // stringa blocco.
p = s = malloc( BLOCK_SIZE ) ;
totread = 0 ;
while ( (curread=fgets(p,BLOCK_SIZE-1,f) > 0 ) {
totread += curread ;
p[BLOCK_SIZE-1] = '\0' ; // terminiamo la stringa per facilita'.
if ( curread == BLOCK_SIZE-1 ) { // oops... strigna piu' lunga
s = realloc( s, totread + BLOCK_SIZE ) ;
p = s + totread ; // p ora punta alla fine della s.
}
}
return s ;
}
ho scritto il codice di getto, quindi controlla i boundaries e la riassegnazione ;-)
Ho evitato copia-copia inutile. L'unico copy potrebbe essere dentro la realloc
Scusa se ti sembra troppo condensato... sono un maniaco della compattezza del codice e della efficienza.
Ciao
leadergl
13-01-2006, 14:20
ti ringrazio, mi sei stato utilissimo....ora giusto per curiosità cosa c'è di sbagliato in questo?
#define string char *
string mid(string origine, int inizio, int fine)
{
int i;
string tmp=(string)malloc(sizeof(fine-inizio)+1);
if (fine>strlen(origine)) fine=strlen(origine);
for (i=inizio;i<=fine;i++)
{
tmp[i]=origine[i];
}
return tmp;
}
prende una stringa in input e ritorna la sottostringa che parte da INIZIO ed arriva a FINE....solo che in uscita dal FOR in pratica è tutto vuoto...credo che mi perdo i puntatori in qualche modo...so che alla fine è un rigo di codice...ma si ho sbagliato, solo che nn so dove...
Fenomeno85
13-01-2006, 14:25
semplicemente perchè quella che hai dichiarato altro non è che una variabile LOCALE della funzione ;) ... quando fai un ret dallo stack tutta quella roba è eliminata.
~§~ Sempre E Solo Lei ~§~
Eviterei di definire una stringa un puntatore... si perde il significato e si scorda il puntatore.
comunque...
string mid(string origine, int inizio, int fine) {
int len = strlen(origine) ;
string s ;
if ( fine-inizio+1 > len ) {
fine = len - 1 ;
s = malloc(fine-inizio+2); // 0-0 e' sempre 1 carattere + 1 per il terminatore
strncpy(s,origine+inizio,fine-inizio+1) ;
s[fine-inizio+1] = '\0';
return s ;
}
semplicemente perchè quella che hai dichiarato altro non è che una variabile LOCALE della funzione ;) ... quando fai un ret dallo stack tutta quella roba è eliminata.
Non e' esatto. La variabile allocata sullo stack e' eliminata, ma il blocco allocato sul heap con la malloc ed il puntatore che viene restituito restano.
Personalmente non sono un fan della allocazione dentro le funzioni... si tende a perdere il controllo di quanto allocato e ci si deve sempre ricordare chi alloca e chi non alloca roba sul heap.
Fenomeno85
13-01-2006, 14:34
Non e' esatto. La variabile allocata sullo stack e' eliminata, ma il blocco allocato sul heap con la malloc ed il puntatore che viene restituito restano.
Personalmente non sono un fan della allocazione dentro le funzioni... si tende a perdere il controllo di quanto allocato e ci si deve sempre ricordare chi alloca e chi non alloca roba sul heap.
si hai ragione ... vado a dormire che è meglio :D
~§~ Sempre E Solo Lei ~§~
Se vuoi sapere in cosa hai sbagliato nel tuo codice originale
for (i=inizio;i<=fine;i++)
{
tmp[i]=origine[i];
}
i non va da 0 a n ma da inizio > 0 a fine > inizio.
Quindi stai scrivendo in tmp[10] quando dovresti partire da tmp[0]....
Ciao.
PS: Mazza.... 4 post in 30 minuti... e non mi sono ancora presentato.
leadergl, occhio che:
#define string char *questo è concettualmente sbagliato, dovresti usare un typedef, cioè:typedef char* string;
Prova infatti a dirmi la differenza tra:
---------
#define string char *
string s1, s2;
---------
e
---------
typedef char* string;
string s1, s2;
---------
;)
string tmp=(string)malloc(sizeof(fine-inizio)+1);Noto curiosamente che usi spesso la sizeof in modo inappropriato. L'espressione sizeof(fine-inizio)+1 vale sempre 5!
Inoltre, nel codice in un post sopra (non te l'ho segnalato subito), hai messo:
sizeof(char[indice+10])
che pur essendo giusto come valore non è ammesso nell'ANSI "C".
leadergl
13-01-2006, 14:58
perfetto, vi ringrazio ;)
ora posso proseguire col mio cavolo di progetto in C...
mi sento un deficiente quando programmo in C :( ...praticità e dimestichezza nell'uso del linguaggio quasi pari a 0
...vabbè....torno al mio albero radicato...
ah..non è che avete sotto-mano qualche algoritmo per riempire un albero radicato caricandolo da file?
...vabbè....torno al mio albero radicato...
ah..non è che avete sotto-mano qualche algoritmo per riempire un albero radicato caricandolo da file?
Ah già ... a proposito dell'albero della Tassonomia! ... in questa settimana sono stato moooolto impegnato e non ho potuto continuare il programmino che volevo fare anche io. Devo trovare un momento (presumibilmente domani) in cui sono tranquillo e "fresco" e poi lo continuo. Ti faccio sapere, ok? :)
leadergl
13-01-2006, 16:31
io lo devo consegnare il 15...cmq il programma è abbastanza avanti nel senso che ho scritto:
1) funzioni gestione lista
2) funzioni gestione albero
3) funzione (con vostre correzioni) per leggere un intero rigo da file
4) funzione (con vostre correzioni) per gestire facilmente la riga letta (in alternativa ho usato la strtok)
5) struttura del file scelta (questa è più semplice da gestire e non ci sono dati ridondanti):
Nome_Nodo_1 | NumeroFigli | Proprieta_1 | ... | Proprieta_N
Nome_Nodo_Figlio_1 | NumeroFigli | Proprieta_1 | ... | Proprieta_N
...
Nome_Nodo_Figlio_M | NumeroFigli | Proprieta_1 | ... | Proprieta_N
Nome_Nodo_2 | NumeroFigli | Proprieta_1 | ... | Proprieta_N
...
Nome_Nodo_N | NumeroFigli | Proprieta_1 | ... | Proprieta_N
quindi le cose da fare sono (nella funzione GeneraAlbero):
1) Riempio Nodo
2) Riempio Lista Proprieta Nodo
3) Il Nodo ha Figli?
SI ---> a) alloco memoria per il figlio
b) leggo rigo da file
c) richiamo la funzione ricorsiva con: tree->figlio=GeneraAlbero(...)
NO ---> a) alloco memoria per il fratello
b) leggo rigo da file
c) richiamo la funzione ricorsiva con: tree->fratello=GeneraAlbero(...)
Che dici va bene logicamente parlando?
...perchè io ho cominciato a perdermi durante la scrittura del codice....a te viene in mente niente?
P.S.
A mia disposizione per un rapido utilizzo ho:
1) freadline -> legge una riga intera da file
2) separa -> permette di estrarre da una stringa delle sottostringhe separate da un carattere speciale
3) mid -> permette di estrarre da una stringa una sottostringa dandone inizio e fine
credo che queste agevolano non poco il lavoro...
non so se è stato detto,
ma perchè invece della "PAUSE" di sistema non viene chiamata una getch() ?
La trovo + efficiente come "pausa"..
Poi, tornando allo scopo del codice, perchè non usare un parser a puntatori con la funzione strstr chiamata sull' End of line?
vabbè, forse non è proprio da dilettanti, però è abbastanza efficiente..
Mah..solo consigli i miei..
Ciao
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.