2009-12-28 10 views
18

Ho due file di testo, Source.txt e Target.txt. La sorgente non verrà mai modificata e contiene N righe di testo. Quindi, voglio eliminare una specifica riga di testo in Target.txt e sostituirla con una specifica riga di testo da Source.txt, so quale numero di linea ho bisogno, in realtà è il numero di riga 2, entrambi i file.Modifica una riga specifica di un file di testo in C#

Mi rifugio qualcosa di simile:

string line = string.Empty; 
int line_number = 1; 
int line_to_edit = 2; 

using (StreamReader reader = new StreamReader(@"C:\source.xml")) 
{ 
    using (StreamWriter writer = new StreamWriter(@"C:\target.xml")) 
    { 
     while ((line = reader.ReadLine()) != null) 
     { 
      if (line_number == line_to_edit) 
      { 
       writer.WriteLine(line); 
      } 

      line_number++; 
     } 
    } 
} 

Ma quando ho aperto il Writer, il file di destinazione vengono cancellati, scrive le righe, ma, una volta aperto, il file di destinazione contiene solo le linee copiate, il il resto si perde

Cosa posso fare?

risposta

38

Non è possibile riscrivere una riga senza riscrivere l'intero file (a meno che le righe non abbiano la stessa lunghezza). Se i tuoi file sono piccoli, allora leggendo l'intero file di destinazione in memoria e poi scrivendolo di nuovo potrebbe avere senso. È possibile farlo in questo modo:

using System; 
using System.IO; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     int line_to_edit = 2; // Warning: 1-based indexing! 
     string sourceFile = "source.txt"; 
     string destinationFile = "target.txt"; 

     // Read the appropriate line from the file. 
     string lineToWrite = null; 
     using (StreamReader reader = new StreamReader(sourceFile)) 
     { 
      for (int i = 1; i <= line_to_edit; ++i) 
       lineToWrite = reader.ReadLine(); 
     } 

     if (lineToWrite == null) 
      throw new InvalidDataException("Line does not exist in " + sourceFile); 

     // Read the old file. 
     string[] lines = File.ReadAllLines(destinationFile); 

     // Write the new file over the old file. 
     using (StreamWriter writer = new StreamWriter(destinationFile)) 
     { 
      for (int currentLine = 1; currentLine <= lines.Length; ++currentLine) 
      { 
       if (currentLine == line_to_edit) 
       { 
        writer.WriteLine(lineToWrite); 
       } 
       else 
       { 
        writer.WriteLine(lines[currentLine - 1]); 
       } 
      } 
     } 
    } 
} 

Se i file sono di grandi dimensioni sarebbe meglio per creare un nuovo file in modo che si può leggere in streaming da un file mentre si scrive all'altro. Ciò significa che non è necessario avere l'intero file in memoria in una volta. Potete farlo in questo modo:

using System; 
using System.IO; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     int line_to_edit = 2; 
     string sourceFile = "source.txt"; 
     string destinationFile = "target.txt"; 
     string tempFile = "target2.txt"; 

     // Read the appropriate line from the file. 
     string lineToWrite = null; 
     using (StreamReader reader = new StreamReader(sourceFile)) 
     { 
      for (int i = 1; i <= line_to_edit; ++i) 
       lineToWrite = reader.ReadLine(); 
     } 

     if (lineToWrite == null) 
      throw new InvalidDataException("Line does not exist in " + sourceFile); 

     // Read from the target file and write to a new file. 
     int line_number = 1; 
     string line = null; 
     using (StreamReader reader = new StreamReader(destinationFile)) 
     using (StreamWriter writer = new StreamWriter(tempFile)) 
     { 
      while ((line = reader.ReadLine()) != null) 
      { 
       if (line_number == line_to_edit) 
       { 
        writer.WriteLine(lineToWrite); 
       } 
       else 
       { 
        writer.WriteLine(line); 
       } 
       line_number++; 
      } 
     } 

     // TODO: Delete the old file and replace it with the new file here. 
    } 
} 

È possibile poi sposta il file una volta che si è certi che l'operazione di scrittura è riuscita (senza excecption è stato gettato e lo scrittore è chiuso).

Si noti che in entrambi i casi è un po 'di confusione che si stia utilizzando l'indicizzazione basata su 1 per i numeri di linea. Potrebbe avere più senso nel codice utilizzare l'indicizzazione basata su 0. È possibile avere un indice basato su 1 nell'interfaccia utente del programma, se lo si desidera, ma convertirlo in un indice 0 prima di inviarlo ulteriormente.

Inoltre, uno svantaggio di sovrascrivere direttamente il vecchio file con il nuovo file è che se fallisce a metà strada si rischia di perdere definitivamente qualsiasi dato non sia stato scritto. Scrivendo prima su un terzo file, si eliminano solo i dati originali dopo essersi assicurati di averne un'altra copia (corretta), in modo da poter recuperare i dati se il computer si blocca a metà.

Un'ultima osservazione: ho notato che i file avevano un'estensione xml. Potresti considerare se ha più senso usare un parser XML per modificare il contenuto dei file invece di sostituire linee specifiche.

2

Quando si crea un file StreamWriter, si crea sempre un file da zero, sarà necessario creare un terzo file e copiare dalla destinazione e sostituire ciò che è necessario, quindi sostituire quello precedente. Ma come posso vedere quello che ti serve è la manipolazione XML, potresti voler usare XmlDocument e modificare il tuo file usando Xpath.

1

Immagino che il sotto dovrebbe funzionare (invece della parte dello scrittore dal tuo esempio). Sono, purtroppo, senza alcun ambiente di compilazione in modo che sia a memoria, ma spero che aiuta

using (var fs = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite))) 
     { 
      var destinationReader = StreamReader(fs); 
      var writer = StreamWriter(fs); 
      while ((line = reader.ReadLine()) != null) 
      { 
       if (line_number == line_to_edit) 
       { 
        writer.WriteLine(lineToWrite); 
       } 
       else 
       { 
        destinationReader .ReadLine(); 
       } 
       line_number++; 
      } 
     } 
2

è necessario aprire il file di output per l'accesso in scrittura piuttosto che utilizzare un nuovo StreamReader, che sovrascrive sempre il file di output.

StreamWriter stm = null; 
fi = new FileInfo(@"C:\target.xml"); 
if (fi.Exists) 
    stm = fi.OpenWrite(); 

Naturalmente, si dovrà ancora cercare di linea corretta nel file di output, che sarà difficile perché non si può leggere da esso, quindi a meno che si conosce già compensato il byte per cercare di, probabilmente vuoi davvero l'accesso in lettura/scrittura.

FileStream stm = fi.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); 

con questo flusso, è possibile leggere fino ad arrivare al punto in cui si desidera apportare modifiche, quindi scrivere. Tieni presente che stai scrivendo byte, non righe, quindi per sovrascrivere una riga dovrai scrivere lo stesso numero di caratteri della riga che desideri modificare.

21

il modo più semplice è:

static void lineChanger(string newText, string fileName, int line_to_edit) 
{ 
    string[] arrLine = File.ReadAllLines(fileName); 
    arrLine[line_to_edit - 1] = newText; 
    File.WriteAllLines(fileName, arrLine); 
} 

utilizzo:

lineChanger("new content for this line" , "sample.text" , 34); 
+0

Assolutamente eccezionale risposta !!!! ... totalmente lavorare qui. in esecuzione in dot net 2.0 ... in Unity3d è: ___ function lineChanger (newText: String, fileName: String, line_to_edit: int) { var arrLine: String [] = File.ReadAllLines (fileName); arrLine [line_to_edit - 1] = newText; File.WriteAllLines (fileName, arrLine); } –

+0

Questo ha 1 problema, che riscrive l'intero file. –

+0

carica anche tutto il file in memoria tutto in una volta. ma è probabilmente l'approccio più semplice e ancora utilizzabile con file di piccole dimensioni. –

Problemi correlati