用C#替换流中的字符串(不覆盖原始文件)

时间:2013-09-16 19:33:22

标签: c# .net string filestream

我有一个文件,我打开流并传递给另一个方法。但是,我想在将流传递给另一个方法之前替换文件中的字符串。所以:

string path = "C:/...";
Stream s = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
//need to replace all occurrences of "John" in the file to "Jack" here.
CallMethod(s);

不应修改原始文件,只修改流。最简单的方法是什么?

...谢谢

3 个答案:

答案 0 :(得分:11)

如果您只是将文件作为行读入,然后处理这些内容,而不是强迫自己坚持Stream,那就简单了,因为流处理文本和二进制文件,以及需要能够一次读取一个字符(这使得这种替换变得非常困难)。如果你一次读一整行(只要你没有多行替换),这很容易。

var lines = File.ReadLines(path)
    .Select(line => line.Replace("John", "Jack"));

请注意,ReadLines仍会对数据进行流式处理,而Select不需要实现整个内容,因此在执行此操作时,您仍然无法将整个文件一次性读入内存

如果您实际上不需要流式传输数据,则可以轻松地将其全部作为一个大字符串加载,执行替换,然后基于该字符串创建流:

string data = File.ReadAllText(path)
    .Replace("John", "Jack");
byte[] bytes = Encoding.ASCII.GetBytes(data);
Stream s = new MemoryStream(bytes);

答案 1 :(得分:2)

这个问题可能有很多好的答案。我会尝试一下我用过的,一直为我和我的同龄人工作。

我建议你创建一个单独的流,比如MemoryStream。从文件流中读取并写入内存。然后,您可以从中提取字符串并替换内容,然后您将提前传递内存流。这样可以确保您不会弄乱原始流,并且您可以随时从中读取原始值,但使用此方法时使用的内存基本上是原来的两倍。

答案 2 :(得分:1)

如果文件中的行非常长,则替换的字符串可能包含换行符,或者存在其他限制,从而在需要流传输时阻止使用File.ReadLines(),那么有一种替代解决方案仅,即使它不是很简单。

实施自己的执行替换的流装饰器(包装器)。即一个基于Stream的类,该类在其构造函数中使用另一个流,以其Read(byte[], int, int)重写从流中读取数据,并在缓冲区中执行替换。有关更多要求和建议,请参见notes to Stream implementers

我们将要替换的字符串称为“ needle”,将源流称为“ haystack”,并将替换字符串“ replacement”。

需要使用干草堆内容的编码(通常为Encoding.UTF8.GetBytes())对针和替换进行编码。在流内部,与StreamReader.ReadLine()不同,数据不会转换为字符串。这样可以防止不必要的内存分配。

简单情况:如果needle和replacement都只是一个字节,则实现只是缓冲区上的简单循环,可以替换所有出现的情况。如果needle是单个字节,而replacement为空(即删除该字节,例如删除行末尾归一化的回车),则这是一个简单的循环,它将fromto的索引保留到缓冲区,然后重写逐字节缓冲。

在更复杂的情况下,请实施 KMP algorithm 进行替换。

  • 将数据从基础流(干草堆)读取到至少与针一样长的内部缓冲区,并在将数据重写到输出缓冲区的同时执行替换。需要内部缓冲区,这样才能在检测到完全匹配之前不发布部分匹配的数据-那么,现在返回并完全删除该匹配为时已晚。

  • 逐字节处理内部缓冲区,将每个字节送入KMP自动机。在每次自动机更新时,将释放的字节写入输出缓冲区中的适当位置。

  • 当KMP检测到匹配项时,将其替换:重置自动机,将其位置保留在内部缓冲区中(这会删除匹配项),并将替换项写入输出缓冲区中。

  • 到达任意一个缓冲区的末尾时,请将内部缓冲区的未写输出和未处理部分(包括当前部分匹配)作为下一次调用该方法的起点,并返回当前输出缓冲区。对该方法的下一次调用将写入剩余的输出,并开始处理当前停止的干草堆的其余部分。

  • 到达干草堆的末端时,释放当前的部分匹配并将其写入输出缓冲区。

请注意在处理所有干草堆数据之前,不要返回空的输出缓冲区,否则会向调用者发出流结束信号,从而截断数据。