2012-06-17 9 views
9

Ho un file di testo che viene aggiunto nel tempo e periodicamente voglio troncarlo in una certa dimensione, ad es. 10 MB, ma mantenendo gli ultimi 10 MB anziché il primo.Come troncare un file con una certa dimensione ma mantenere la sezione finale?

Esiste un modo intelligente per farlo? Immagino che dovrei cercare il punto giusto, leggere da lì in un nuovo file, eliminare il vecchio file e rinominare il nuovo file con il vecchio nome. Qualche idea o codice di esempio migliore? Idealmente non leggerei l'intero file in memoria perché il file potrebbe essere grande.

prega nessun suggerimento su come utilizzare Log4Net ecc

risposta

4

Se stai bene con solo leggendo l'ultimo 10 MB nella memoria, questo dovrebbe funzionare:

using(MemoryStream ms = new MemoryStream(10 * 1024 * 1024)) { 
    using(FileStream s = new FileStream("yourFile.txt", FileMode.Open, FileAccess.ReadWrite)) { 
     s.Seek(-10 * 1024 * 1024, SeekOrigin.End); 
     s.CopyTo(ms); 
     s.SetLength(10 * 1024 * 1024); 
     s.Position = 0; 
     ms.Position = 0; // Begin from the start of the memory stream 
     ms.CopyTo(s); 
    } 
} 
+4

1 Potrebbe anche essere necessario un ms.Position = 0 prima della finale CopyTo() (io ho usato un altro FileStream non MemoryStream) – StuartLC

+0

@nonnb: buon punto; Grazie. – Ryan

+0

@minitech sembra che il tuo codice funzioni SOLO da .NET 4.0 in su. L'ho scoperto mentre scrivevo sul mio progetto .NET 3.5. – Alex

4

Non è necessario leggere l'intero file prima di scrivere, soprattutto se non si sta scrivendo in un file diverso. Puoi lavorare in blocchi; leggere un po ', scrivere, leggere un po' di più, scrivere di nuovo. In effetti, è così che tutto l'I/O è fatto comunque. Soprattutto con i file più grandi non hai mai veramente voglia di leggerli tutti in una volta.

Ma ciò che si propone è l'unico modo per rimuovere i dati dall'inizio di un file. Devi riscriverlo. Raymond Chen has a blog post on exactly that topic, too.

1

È possibile leggere il file in un flusso binario e utilizzare il metodo di ricerca per recuperare solo gli ultimi 10 MB di dati e caricarli in memoria. Quindi salverai questo stream in un nuovo file ed eliminerai quello vecchio. I dati di testo potrebbero essere troncati, quindi è necessario decidere se questo è eccezionale.

Guardate qui per un esempio del metodo Seek:

http://www.dotnetperls.com/seek

+0

Sì l'esempio di codice fornito da MiniTech potrebbe essere l'implementazione di mia porposition :) –

2

ho provato la soluzione da "false" ma non funziona per me, taglia il file ma mantiene l'inizio, non la fine.

Ho il sospetto che CopyTo copia l'intero flusso invece di iniziare per la posizione del flusso. Ecco come ho fatto funzionare:

int trimSize = 10 * 1024 * 1024; 
using (MemoryStream ms = new MemoryStream(trimSize)) 
{ 
    using (FileStream s = new FileStream(logFilename, FileMode.Open, FileAccess.ReadWrite)) 
    { 
     s.Seek(-trimSize, SeekOrigin.End); 

     byte[] bytes = new byte[trimSize]; 
     s.Read(bytes, 0, trimSize); 
     ms.Write(bytes, 0, trimSize); 

     ms.Position = 0; 
     s.SetLength(trimSize); 
     s.Position = 0; 
     ms.CopyTo(s); 
    } 
} 
+0

Lo confermo nel mio ambiente (Win8.1 Pro con WMC 64 bit, .NET 4.6.1, C#). Nel mio caso particolare, 'ms.CopyTo' non funziona correttamente. Modificato con 'fs.Write (ms.GetBuffer(), 0, trimSize);' (Sto usando [questo] (http://stackoverflow.com/a/11072529/1273042) soluzione). +1 per segnalare il problema (: – mishamosher

Problemi correlati