PDA

View Full Version : [C#] Leggere/Scrivere da file al contrario


guylmaster
21-05-2011, 01:56
Salve a tutti,
ho un problema, ovvero devo scrivere delle informazioni su un file, riga per riga, e poi devo poter prendere le 100 informazioni più recenti.

Presupponendo che non so quante righe ci possono essere su un file e che potrebbero essere anche tantissime (quindi no l'idea di leggersele tutte su un array e poi leggersi gli indici all'incotrario) allora mi chiedo se c'è un modo per ovviare il problema:

- O scrivendo sul file "all'incontrario", ovvero invece di appendere a fine file ogni nuova stringa di scriverla in testa;

- O leggendo dalla fine del file verso l'inizio;


Io attualmente per scrivere utilizzo il seguente codice:


path = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
fs = new FileStream(path + @nomefile, FileMode.Append, FileAccess.Write);
streamwriter = new StreamWriter(fs);
streamwriter.WriteLine(linea);
streamwriter.Close();



Non c'è qualche modo rapido ed indolore per dirgli semplicemente di scrivere a inizio file e non a fine file?

Per leggere utilizzo analogalmente uno streamreader ed il corrispettivo metodo readline.

P.S: Contate che sto programmando un applicazione per smartphone e quindi dispongo della compact framework 3.5 e visual studio 2008, quindi magari non ho tutti i controlli possibili ed immaginabili.

Vi ringrazio in anticipo,
guylmaster.

WarDuck
21-05-2011, 10:13
Potresti usare una struttura dati temporanea tipo Stack e poi scansionare quest'ultima scrivendo ogni elemento (riga) sul file ;).

guylmaster
21-05-2011, 11:53
Potresti usare una struttura dati temporanea tipo Stack e poi scansionare quest'ultima scrivendo ogni elemento (riga) sul file ;).

Il problema è che è un programma che deve stare sempre accesso, ricevere informazioni e scrivere su file. Per fare una cosa del genere dovrei avere un numero finito di informazioni, in modo che uno aspetta di leggerle tutte e poi le scrive su file. Se uno invece tiene il programma accesso per 8:00 ore che razza di stack deve fare? sopratutto poi se si blocca il programma perde 8 ore di dati..

guylmaster
21-05-2011, 14:26
Ho provato ad adottare questo escamotage:
-Scrivo la nuova riga su un file temporaneo;
-Accodo nel file temporaneo le vecchie righe;
-cancello il vecchio file e faccio diventare il file temporaneo in file reale;

E' un operazione dispendiosa ma dovrebbe permettermi sostanzialmente di far apprire in testa l'ultima riga scritta, peccato che al momento di dover cancellare il file (non il temoporaneo, quello diciamo reale) mi dice sempre che il file è in uso e non può farlo e non mi raccapacito del perchè contando che:

-poco prima stavo leggendo e se il file era in uso in qualche altra parte del programma non avrei manco letto;
-subito dopo la lettura ho chiuso tutti gli stream;

Il codice è questo se riuscite a darmi qualche dritta sul dove sbaglio mi fate sicuramente un grandissimo favore:


//mi creo uno stremwriter temporaneo
path = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
string nomefile = "\\temp.txt";
FileStream temp_fs_writer = new FileStream(path + @nomefile, FileMode.Append, FileAccess.Write);
StreamWriter temp_stremwriter = new StreamWriter(temp_fs_writer);
temp_stremwriter.WriteLine(linea);

//mi creo uno streamreader temporaneo
FileStream temp_fs_reader = new FileStream(path + this.@file_scrittura, FileMode.OpenOrCreate, FileAccess.Read);
StreamReader temp_streamreader = new StreamReader(temp_fs_reader);
string lettura = "";
while (!temp_streamreader.EndOfStream)
{
lettura = temp_streamreader.ReadLine();
temp_stremwriter.WriteLine(lettura);
}

temp_streamreader.Close();
temp_stremwriter.Close();
temp_fs_reader.Close();
temp_fs_writer.Close();

File.Delete(path + this.@file_scrittura);
File.Move(path + @nomefile, path + this.@file_scrittura);

Don[ITA]
21-05-2011, 15:07
Potresti usare una lista lincata: ogni volta che ricevi un dato lo aggiungi in testa alla lista e scrivi il tuo file, se stai inserendo il 101 esimo dato rimuovi la coda della lista, che in pseudo codice potrebbe essere qualcosa tipo:

aggiungi(L, dato)
begin:
size := length(L)
if (size + 1 > 100)
remove(L) <--- rimuovi l'ultimo elemento della lista
add(L, dato) <--- aggiungi il dato in testa alla lista L
salva(dato) <--- scrivi il dato sul file
end;

Così in ogni istante hai al più una lista di dimensione 100 che contiene gli ultimi 100 dati ricevuti :D

cya

WarDuck
21-05-2011, 15:54
Se il programma deve girare per 8 ore secondo me ti conviene fare come ti abbiamo detto io e Don, fissando un intervallo programmato per fare le scritture (ad esempio ogni 5 minuti sovrascrivi il file).

Al massimo in caso di blocco perdi gli ultimi 5 minuti.

Comunque, c'è una necessità reale di scrivere l'evento più recente in cima al file?

Perché secondo me è solo una complicazione che potresti risparmiare.

guylmaster
21-05-2011, 16:27
Ma secondo voi il codice mio di sopra come mai mi dice che il file è in uso se lo chiudo?

A me serve scriverlo all'incontrario altrimenti poi come faccio a prendere gli ultimo 100?

Quello della lista linkata non è proprio il massimo comunque. Ripeto non vorrei rischiare la perdita di dati.

tomminno
21-05-2011, 18:45
Il problema è che è un programma che deve stare sempre accesso, ricevere informazioni e scrivere su file. Per fare una cosa del genere dovrei avere un numero finito di informazioni, in modo che uno aspetta di leggerle tutte e poi le scrive su file. Se uno invece tiene il programma accesso per 8:00 ore che razza di stack deve fare? sopratutto poi se si blocca il programma perde 8 ore di dati..

Se il programma è sempre attivo quale sarebbe il problema a tenersi in memoria un'array con le ultime 100 informazioni inserite nel file?
Alla chiusura del programma eventualmente le salvi in un file separato così che al riavvio non hai bisogno di riparserizzare tutto il file principale, ma ti leggi questo file con le ultime informazioni inserite alla precedente esecuzione.

Quanto al tuo problema dell'impossibilità di cancellare un file aperto in precedenza è un fenomeno che ho notato anch'io. Io proverei comunque ad usare lo using ovunque serva in modo che venga fatto il dispose oltre al close degli oggetti.
In ogni caso ho verificato molto spesso che su Windows passa qualche secondo tra quando uno esegue il dispose e l'effettivo rilascio del file che risulta pertanto non cancellabile perchè in uso, suppongo sia tutto merito del gc.
Generalmente in questi casi utilizzo un thread separato che provvede alla cancellazione dei file in maniera asincrona, che consente la gestione arbitraria dei ritentativi di cancellazione, senza bloccare il flusso principale.

PGI-Bis
21-05-2011, 19:06
La cosa affascinante è che qui sembra quasi che leggere le righe all'inizio del file e leggerle partendo dalla fine sia qualcosa di diverso.

No, è uguale: l'unica cosa che cambia è che nel buffer di lettura anziché fare un "enqueue" si fa un "push".

In pseudo codice sarebbe:

buffer = un buffer di caratteri
file = un ipotetico flusso di lettura di caratteri
file.position(file.size-1)
while(file.position >= 0)
char c = file.readChar
file.position -= 1
if(c == interruzione di linea)
notificaLinea(buffer)
buffer.clear
else
buffer.push(c)

Diversa è la questione della scrittura: coi filesystem a me noti i file sono fatti per crescere in una sola direzione.

In questo contesto scrivere il file al contrario è una pessima idea.

A seconda poi della frequenza con cui l'operazione di lettura deve essere fatta, potrebbe valere la pena di usare un meccanismo simile a quello indicato da Don[ITA], vale a dire un logger.

guylmaster
21-05-2011, 20:14
Dunque è un operazione che dovrebbe avvenire ogni 5 minuti, per roba come 8 ore al giorno, però l'idea che se succede qualcosa ti perdi 100 rievazioni non mi piace molto.

Comunque ho capito dov'era l'errore infimo del file non chiuso. C'era una classe A che specializzava una classe B e la classe A creava uno streaming su quel file, ed io a controllare e ricontrollare le classi che entravano in gioco non me ne ero accorto.

Ora va, anche se c'è da dire che già con meno di un 100 di informazioni prememorizzate da scansionare, la creazione dello storico con il mio metodo mi impiega già 1-2 secondi. Temo che se nel file ci mettessi chesso 1 mese di informazioni, che corrisponderebbero ad una riga di file ogni 5 minuti per 8 ore al giorno, finirebbe per metterci una vita a scandirsi il file e leggere le ultime 100.

Devo provare a sovraccaricare il file e vedere di quanto e palpabile questo rallentamento.

WarDuck
21-05-2011, 21:44
Dunque è un operazione che dovrebbe avvenire ogni 5 minuti, per roba come 8 ore al giorno, però l'idea che se succede qualcosa ti perdi 100 rievazioni non mi piace molto.

Comunque ho capito dov'era l'errore infimo del file non chiuso. C'era una classe A che specializzava una classe B e la classe A creava uno streaming su quel file, ed io a controllare e ricontrollare le classi che entravano in gioco non me ne ero accorto.

Ora va, anche se c'è da dire che già con meno di un 100 di informazioni prememorizzate da scansionare, la creazione dello storico con il mio metodo mi impiega già 1-2 secondi. Temo che se nel file ci mettessi chesso 1 mese di informazioni, che corrisponderebbero ad una riga di file ogni 5 minuti per 8 ore al giorno, finirebbe per metterci una vita a scandirsi il file e leggere le ultime 100.

Devo provare a sovraccaricare il file e vedere di quanto e palpabile questo rallentamento.

E' semplice, ogni 5 minuti fai un PUSH su una struttura dati stack e poi sovrascrivi il file con le ultime 100 informazioni (ammesso che tu debba ripeto necessariamente scrivere "al contrario").

Al massimo perdi gli ultimi 5 minuti.