在C#中编辑特定的文本文件行

我有两个文本文件,Source.txt和Target.txt。 源文件将不会被修改,并且包含N行文本。 所以,我想删除Target.txt中的一行文本,并从Source.txt中replace为特定的文本行,我知道我需要的行数,实际上是行号2,两个文件。

我有这样的事情:

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++; } } } 

但是当我打开Writer时,目标文件被擦除,它写入行,但是打开时,目标文件只包含复制的行,其余的会丢失。

我能做什么?

如果不重写整个文件,则不能重写一行(除非行长恰好相同)。 如果你的文件很小,那么把整个目标文件读入内存然后再写出来可能是有道理的。 你可以这样做:

 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]); } } } } } 

如果你的文件很大,最好创build一个新文件,这样当你写入另一个文件时,你可以从一个文件中读取stream。 这意味着你不需要一次把整个文件存储在内存中。 你可以这样做:

 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. } } 

一旦确定写入操作成功后,您可以随后移动文件(不会引发写入和写入器closures)。

请注意,在这两种情况下,对于行号使用从1开始的索引是有点令人困惑的。 在代码中使用基于0的索引可能更有意义。 如果您愿意,您可以在程序的用户界面中使用基于1的索引,但在将其发送到进一步之前,将其转换为0索引。

另外,用新文件直接覆盖旧文件的缺点是,如果中途失败,那么可能会永久丢失任何未写入的数据。 通过先写入第三个文件,在确定已经有另一个(更正过的)副本之后,您只能删除原始数据,以便在计算机崩溃一半时恢复数据。

最后一句:我注意到你的文件有一个xml扩展名。 您可能需要考虑是否更有意义的是使用XMLparsing器来修改文件的内容而不是replace特定的行。

最简单的方法是:

 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); } 

用法:

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

您需要打开输出文件进行写访问,而不是使用新的StreamReader,它总是覆盖输出文件。

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

当然,你仍然需要在输出文件中寻找正确的行,因为你不能读取它,所以很难读取,所以除非你已经知道要寻找的字节偏移,否则你可能真的想读/写访问。

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

用这个stream,你可以阅读,直到你想要进行更改,然后写。 请记住,您正在写字节,而不是行,所以要覆盖一行,您将需要编写与要更改的行相同数量的字符。

当你创build一个StreamWriter时候,总是从头创build一个文件,你将不得不创build第三个文件并从目标文件中复制,并replace你需要的文件,然后replace旧文件。 但是我可以看到你需要的是XML操作,你可能想使用XmlDocument并使用Xpath修改你的文件。

我想下面应该工作(而不是你的例子中的作家部分)。 我很遗憾没有构build环境,所以这是从记忆,但我希望它可以帮助

 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++; } }