Newsgroup: it.comp.lang.c
Titolo: leggere un file da una certa linea ad un'altra

Ciao a tutti, ho la necessita' di leggere un file che e' diviso in
sezioni. L'ultima sezione e' l'indice e indica (appunto) il numero di
linea in cui cominciano le altre sezioni.
Le domande a questo punto sono:
1- si puo' leggere un file (di testo) a ritroso? Riga per riga
2- come mi muovo alla riga desiderata (letta nell'indice)?
Magari ho capito male io, ma con fseek non mi muovo di linea in linea ma
di n bytes in n bytes, giusto? E a priori io come faccio a capire quanti
bytes ci sono fino alla linea x? Bah... che casino che faccio!
-- 
Davide Cittaro
Drop WINDOWS if you want to reply

 2- come mi muovo alla riga desiderata (letta nell'indice)?
Ponendo che non puoi/vuoi leggerti tutto il file in memoria, la 
soluzione imho migliore è costruirti almeno un indice in memoria. 
Ovvero, la prima volta scorri tutto il file riga per riga e per ciascuna 
riga ti salvi la posizione. Dopodichè ogni volta che devi leggere una 
riga ti posizioni direttamente a inizio lettura.
Altrimenti, per raggiungere la riga i-esima devi ogni volta ripartire 
daccapo e leggerti i-1 righe prima di avere quella giusta.

Davide Cittaro ha scritto:

Hai capito giusto: fseek ti porta a un certo BYTE del file. Ciò detto,
leggere a ritroso BYTE PER BYTE è semplicissimo:
   FILE * f = fopen("pippo.txt", "r");
   fseek(f, 1, SEEK_END);
   do
   {
      int c = getc(f);
      //... fai con c quello che vuoi
   }
   while (fseek(f, ftell(f) - 2, SEEK_CUR));
La prima fseek() ti porta al byte precedente la fine del file (cioè
all'ultimo byte); la seconda fseek ti porta indietro di due byte (cioè ti
porta al byte precedente all'ultimo che hai letto: la funzione ftell() ti dà
la posizione corrente).
Per leggere a ritroso riga per riga, devi implementare qualcosa di simile a
una "fgets() all'incontrario", cioè usi lo stesso loop qui sopra ma
comportandoti in modo particolare quando il carattere letto è '\n'
(new-line). Ma non vado oltre a sviluppare questa idea perché mi pare
completamente sbomballata...
Quand'anche tu, in un modo o nell'altro, riuscissi a leggerti questo indice
contenuto nel file, che te ne fai? Mettiamo che l'indice ti dica che la
sezione 23 inizia alla riga 426, come fai a spostarti alla riga 426? Per
fare una fseek() all'inizio di quella riga, ti servirebbe sapere la somma
delle lunghezze di tutte le righe da 1 a 425...
Secondo me, per fare quello che hai descritto non ti serve leggere
all'incontrario ma, piuttosto, sviluppare il suggerimento che t'ha già dato
Vincent Vega: scorri l'intero file salvandoti in un indice la posizione
d'inizio di ogni riga.
Il primo byte della prima riga è, ovviamente, alla posizione 0. Il primo
byte delle altre righe è quello che segue il precedente carattere '\n'.
L'indice delle righe non sarà altro che un array di long; siccome (suppongo)
non sai a priori quante righe ci sono nel file, sarà bene che questo array
sia allocato dinamicamente usando malloc() e realloc(). Nell'esempio che
segue, ho fatto una riallocazione ogni cinquantina di righe, in modo da non
sprecare troppo tempo per l'allocazione e, contemporaneamente, non sprecare
troppa memoria con righe allocate ma non usate (al massimo ci sarà uno
spreco di 49 elementi).
Leggi che ti leggi arriverai finalmente all'indice delle sezioni alla fine
del file: qui cambi modalità di lettura e ti carichi l'indice. Siccome non
hai spiegato come si riconosce l'indice, nell'esempio che segue (codice
assolutamente non testato) supporrò che si riconosca da una riga che inizia
per '#':
   FILE * f = fopen("pippo.txt", "r");
   int c;
   long pos;
   long * lines = malloc(lines, sizeof(long) * 50);
   long lin = 0;
   lines[lin] = 0;
   for (pos = 0; (c = fgetc(f)) != EOF; pos++)
   {
      if (c == '#')
      {
         /* Ecco l'indice delle sezioni! */
         ungetc(c, f);
         break;
      }
      if (pos != 0 && (pos % 50) == 0)
         lines = realloc(lines, sizeof(long) * pos + 50);
      if (c == '\n' || pos == 0)
         lines[++lin] = pos + 1;
   }
   //... Leggiti l'indice delle sezioni
A questo punto, quando l'indice delle sezioni ti dice che la sezione 23
inizia alla riga 426, tu non fai altro che consultare l'elemento 425
dell'indice delle righe (il tuo array lines[]) dove troverari, per esempio,
il numero 45738, che è il valore che devi passare alla fseek() per andare
all'inizio della 426ª riga:
   fseek(f, lines[425], SEEK_SET);
--
Cingar
--------------------------------
Inviato via 
http://arianna.libero.it/usenet/

 lines = realloc(lines, sizeof(long) * pos + 50);
Oooops!
       if (pos != 0 && (lin % 50) == 0)
          lines = realloc(lines, sizeof(long) * (lin + 50));
Ma ci saranno millanta altre boiate anche peggiori: è solo ...ehm... uno
"schizzo".


 wrote:

