13

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

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

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

谢谢...

4

3 回答 3

15

如果您只是将文件作为行读取,然后处理这些行,而不是强迫自己坚持使用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);
于 2013-09-16T19:36:51.953 回答
2

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

我建议您创建一个单独的流,例如MemoryStream. 从您的文件流中读取并写入内存。然后,您可以从中提取字符串并替换内容,然后将内存流提前传递。这样可以确保您没有弄乱原始流,并且您可以在需要时从中读取原始值,尽管使用此方法基本上使用了两倍的内存。

于 2013-09-16T19:37:49.270 回答
1

如果文件有非常长的行,被替换的字符串可能包含换行符,或者File.ReadLines()在需要流式传输时有其他限制阻止使用,则有一个仅使用流的替代解决方案,即使它不是微不足道的。

实现您自己的执行替换的流装饰器(包装器)。即基于它的类Stream在其构造函数中采用另一个流,在其Read(byte[], int, int)覆盖中从流中读取数据并在缓冲区中执行替换。有关进一步的要求和建议,请参阅Stream 实施者的说明。

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

针和替换需要使用 haystack 内容的编码进行编码(通常是Encoding.UTF8.GetBytes())。在流内部,数据不会转换为字符串,这与StreamReader.ReadLine(). 因此避免了不必要的内存分配。

简单案例:如果 needle 和 replacement 都只是一个字节,则实现只是一个简单的缓冲区循环,替换所有出现的地方。如果 needle 是单个字节并且替换为空(即删除字节,例如删除回车以进行行尾规范化),则它是一个简单的循环维护fromto对缓冲区的索引,逐字节重写缓冲区。

在更复杂的情况下,实现KMP 算法来执行替换。

  • 将数据从底层流(haystack)读取到至少与 needle 一样长的内部缓冲区,并在将数据重写到输出缓冲区的同时执行替换。需要内部缓冲区,以便在检测到完全匹配之前不发布来自部分匹配的数据——然后,返回并完全删除匹配就为时已晚。

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

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

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

  • 当 haystack 结束时,释放当前部分匹配并将其写入输出缓冲区。

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

于 2020-10-27T10:18:19.720 回答