2009-08-27 20 views
26

Qual è il modo migliore per aggiungere del testo all'inizio di un file utilizzando C#?C#: prepending all'inizio di un file

Non sono riuscito a trovare un modo semplice per farlo, ma ho trovato un paio di soluzioni.

  1. Aprire un nuovo file, scrivere il testo che volevo aggiungere, aggiungere il testo dal vecchio file alla fine del nuovo file.

  2. Poiché il testo che voglio aggiungere dovrebbe essere inferiore a 200 caratteri, stavo pensando che potrei aggiungere caratteri di spazio bianco all'inizio del file e quindi sovrascrivere lo spazio bianco con il testo che volevo aggiungere.

Qualcun altro ha incontrato questo problema e, in caso affermativo, che cosa hai fatto?

+1

Interessante domanda. Chiaramente il framework (e senza dubbio il file system sottostante) è append-biased, poiché File.AppendText e File.AppendAllText sono forniti senza i corrispondenti metodi di antefatto. Ha senso: i byte possono essere aggiunti senza "spostare" il contenuto esistente. – harpo

+0

hmm ok, quindi le risposte sembrano favorire l'opzione 1 (utilizzando un file temporaneo o leggendo in memoria). C'è un difetto nell'usare l'opzione 2? – Grace

+0

Puoi controllare questo http://stackoverflow.com/questions/1008742/adding-text-to-beginning-and-end-of-file-in-c post su Aggiunta di testo all'inizio e alla fine del file in C#. È per file XML, ma facilmente modificabile per il tuo uso. – SwDevMan81

risposta

9

L'aggiunta all'inizio di un file (anteporre anziché l'aggiunta) in genere non è un'operazione supportata. Le tue opzioni numero 1 vanno bene. Se non è possibile scrivere un file temporaneo, è possibile estrarre l'intero file in memoria, pre-inviare i dati all'array di byte e quindi sovrascriverli (ciò è possibile solo se i file sono piccoli e non è necessario avere un mazzo in memoria in una volta perché prepending dell'array non è necessariamente facile senza una copia).

+2

Oh, giusto "anteponi". Grazie! Non riuscivo a pensare alla parola corretta. – Grace

2

Penso che il modo migliore sia creare un file temporaneo. Aggiungi il tuo testo quindi leggi il contenuto del file originale aggiungendolo al file temporaneo. Quindi puoi sovrascrivere l'originale con il file temporaneo.

+8

Non sovrascrivere il file originale, che eliminerà ACL e ADS. Usa File.Replace. –

+0

So che questo è vecchio. ACL = Lista controllo accessi? ANNUNCI =? – GibralterTop

1

Si dovrebbe essere in grado di farlo senza aprire un nuovo file. Utilizzare il seguente metodo File:

public static FileStream Open(
    string path, 
    FileMode mode, 
    FileAccess access 
) 

Assicurarsi di specificare FileAccess.ReadWrite.

Utilizzando il FileStream restituito da File.Open, leggere tutti i dati esistenti nella memoria. Quindi reimpostare il puntatore all'inizio del file, scrivere i nuovi dati, quindi scrivere i dati esistenti.

(Se il file è grande e/o si sospetta l'utilizzo di troppa memoria, è possibile farlo senza dover leggere l'intero file in memoria, ma l'implementazione è lasciata come esercizio al lettore.)

0

Inserire il contenuto del file in una stringa. Aggiungi nuovi dati che desideri aggiungere all'inizio del file a quella stringa - string = newdata + string. Quindi sposta la posizione di ricerca del file su 0 e scrivi la stringa nel file.

1
// The file we'll prepend to 
string filePath = path + "\\log.log"; 

// A temp file we'll write to 
string tempFilePath = path + "\\temp.log"; 

// 1) Write your prepended contents to a temp file. 
using (var writer = new StreamWriter(tempFilePath, false)) 
{ 
    // Write whatever you want to prepend 
    writer.WriteLine("Hi"); 
} 

// 2) Use stream lib methods to append the original contents to the Temp 
// file. 
using (var oldFile = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read)) 
{ 
    using (var tempFile = new FileStream(tempFilePath, FileMode.Append, FileAccess.Write, FileShare.Read)) 
    { 
     oldFile.CopyTo(tempFile); 
    } 
} 

// 3) Finally, dump the Temp file back to the original, keeping all its 
// original permissions etc. 
File.Replace(tempFilePath, filePath, null); 

Anche se quello che si sta scrivendo è piccolo, il file Temp ottiene l'intero file originale allegata alla prima del .Rimontare(), in modo che non ha bisogno di essere su disco.

Si noti che questo codice non è thread-safe; se più di un thread accede a questo codice, è possibile perdere le scritture nello scambio di file in corso qui. Detto questo, è anche piuttosto costoso, quindi ti consigliamo di accedere comunque ad esso - passa le scritture tramite più provider a un buffer, che periodicamente si svuota tramite questo metodo anteposto su un singolo thread Consumer.

1

Sì, in pratica si può usare qualcosa di simile:

public static void PrependString(string value, FileStream file) 
{ 
    var buffer = new byte[file.Length]; 

    while (file.Read(buffer, 0, buffer.Length) != 0) 
    { 
    } 

    if(!file.CanWrite) 
     throw new ArgumentException("The specified file cannot be written.", "file"); 

    file.Position = 0; 
    var data = Encoding.Unicode.GetBytes(value); 
    file.SetLength(buffer.Length + data.Length); 
    file.Write(data, 0, data.Length); 
    file.Write(buffer, 0, buffer.Length); 
} 

public static void Prepend(this FileStream file, string value) 
{ 
    PrependString(value, file); 
} 

Poi

using(var file = File.Open("yourtext.txt", FileMode.Open, FileAccess.ReadWrite)) 
{ 
    file.Prepend("Text you want to write."); 
} 

Non veramente efficace anche se in caso di file di grandi dimensioni.

24

Questo funziona per me, ma per file di piccole dimensioni. Probabilmente non è una soluzione molto buona altrimenti.

string currentContent = String.Empty; 
if (File.Exists(filePath)) 
{ 
    currentContent = File.ReadAllText(filePath); 
} 
File.WriteAllText(filePath, newContent + currentContent); 
+1

è quello che farei anche io .. facile, sporco e semplice ... – SolidSnake

1

utilizzare questa classe:

public static class File2 
{ 
    private static readonly Encoding _defaultEncoding = new UTF8Encoding(false, true); // encoding used in File.ReadAll*() 
    private static object _bufferSizeLock = new Object(); 
    private static int _bufferSize = 1024 * 1024; // 1mb 
    public static int BufferSize 
    { 
     get 
     { 
      lock (_bufferSizeLock) 
      { 
       return _bufferSize; 
      } 
     } 
     set 
     { 
      lock (_bufferSizeLock) 
      { 
       _bufferSize = value; 
      } 
     } 
    } 

    public static void PrependAllLines(string path, IEnumerable<string> contents) 
    { 
     PrependAllLines(path, contents, _defaultEncoding); 
    } 

    public static void PrependAllLines(string path, IEnumerable<string> contents, Encoding encoding) 
    { 
     var temp = Path.GetTempFileName(); 
     File.WriteAllLines(temp, contents, encoding); 
     AppendToTemp(path, temp, encoding); 
     File.Replace(temp, path, null); 
    } 

    public static void PrependAllText(string path, string contents) 
    { 
     PrependAllText(path, contents, _defaultEncoding); 
    } 

    public static void PrependAllText(string path, string contents, Encoding encoding) 
    { 
     var temp = Path.GetTempFileName(); 
     File.WriteAllText(temp, contents, encoding); 
     AppendToTemp(path, temp, encoding); 
     File.Replace(temp, path, null); 
    } 

    private static void AppendToTemp(string path, string temp, Encoding encoding) 
    { 
     var bufferSize = BufferSize; 
     char[] buffer = new char[bufferSize]; 

     using (var writer = new StreamWriter(temp, true, encoding)) 
     { 
      using (var reader = new StreamReader(path, encoding)) 
      { 
       int bytesRead; 
       while ((bytesRead = reader.ReadBlock(buffer,0,bufferSize)) != 0) 
       {     
        writer.Write(buffer,0,bytesRead); 
       } 
      } 
     } 
    } 
} 
1

anteporre:

private const string tempDirPath = @"c:\temp\log.log", tempDirNewPath = @"c:\temp\log.new"; 

     StringBuilder sb = new StringBuilder(); 
     ... 
     File.WriteAllText(tempDirNewPath, sb.ToString()); 
     File.AppendAllText(tempDirNewPath, File.ReadAllText(tempDirPath)); 
     File.Delete(tempDirPath); 
     File.Move(tempDirNewPath, tempDirPath); 
     using (FileStream fs = File.OpenWrite(tempDirPath)) 
     { //truncate to a reasonable length 
      if (16384 < fs.Length) fs.SetLength(16384); 
      fs.Close(); 
     } 
1

Il seguente algoritmo può risolvere il problema abbastanza facilmente, è più efficiente per qualsiasi dimensione di file, incluso molto grande di testo file:

string outPutFile = @"C:\Output.txt"; 
string result = "Some new string" + DateTime.Now.ToString() + Environment.NewLine; 
StringBuilder currentContent = new StringBuilder(); 
List<string> rawList = File.ReadAllLines(outPutFile).ToList(); 
foreach (var item in rawList) { 
    currentContent.Append(item + Environment.NewLine); 
}    
File.WriteAllText(outPutFile, result + currentContent.ToString()); 
1

utilizzando due flussi, puoi farlo sul posto, ma tieni presente che questo continuerà a ripetere l'intero file su ogni aggiunta

using System; 
using System.IO; 
using System.Text; 

namespace FilePrepender 
{ 
    public class FilePrepender 
    { 
     private string file=null; 
     public FilePrepender(string filePath) 
     { 
      file = filePath; 
     } 
     public void prependline(string line) 
     { 
      prepend(line + Environment.NewLine); 
     } 
     private void shiftSection(byte[] chunk,FileStream readStream,FileStream writeStream) 
     { 
      long initialOffsetRead = readStream.Position; 
      long initialOffsetWrite= writeStream.Position; 
      int offset = 0; 
      int remaining = chunk.Length; 
      do//ensure that the entire chunk length gets read and shifted 
      { 
       int read = readStream.Read(chunk, offset, remaining); 
       offset += read; 
       remaining -= read; 
      } while (remaining > 0); 
      writeStream.Write(chunk, 0, chunk.Length); 
      writeStream.Seek(initialOffsetWrite, SeekOrigin.Begin); 
      readStream.Seek(initialOffsetRead, SeekOrigin.Begin); 
     } 
     public void prepend(string text) 
     { 
      byte[] bytes = Encoding.Default.GetBytes(text); 
      byte[] chunk = new byte[bytes.Length]; 
      using (FileStream readStream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) 
      { 
       using(FileStream writeStream = File.Open(file, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite)) 
       { 
        readStream.Seek(0, SeekOrigin.End);//seek chunk.Length past the end of the file 
        writeStream.Seek(chunk.Length, SeekOrigin.End);//which lets the loop run without special cases 
        long size = readStream.Position; 
        //while there's a whole chunks worth above the read head, shift the file contents down from the end 
        while(readStream.Position - chunk.Length >= 0) 
        { 
         readStream.Seek(-chunk.Length, SeekOrigin.Current); 
         writeStream.Seek(-chunk.Length, SeekOrigin.Current); 
         shiftSection(chunk, readStream, writeStream); 
        } 
        //clean up the remaining shift for the bytes that don't fit in size%chunk.Length 
        readStream.Seek(0, SeekOrigin.Begin); 
        writeStream.Seek(Math.Min(size, chunk.Length), SeekOrigin.Begin); 
        shiftSection(chunk, readStream, writeStream); 
        //finally, write the text you want to prepend 
        writeStream.Seek(0,SeekOrigin.Begin); 
        writeStream.Write(bytes, 0, bytes.Length); 
       } 
      } 
     } 
    } 
}