使用一个内存流进行多个文件搜索迭代

Posted

技术标签:

【中文标题】使用一个内存流进行多个文件搜索迭代【英文标题】:Using one memorystream for multiple file search iterations 【发布时间】:2011-05-06 08:22:01 【问题描述】:

我有几种方法,每个方法都将操作应用于文本文件,其中下一个操作需要上一个操作的结果作为输入:

private TextReader input = new StreamReader("input.txt");
private TextWriter output = new StreamWriter("output.txt");
MemoryStream result_1 = new MemoryStream();
MemoryStream result_2 = new MemoryStream();

Operation_1(input, ref result_1);
Operation_2(result_1, ref result_2);
Operation_3(result_2, output);

Operation_1 的代码:

   private void Operation_1(TextReader input, ref MemoryStream output)
    
        TextWriter outputWriter = new StreamWriter(output);
        String line;

        while (input.Peek() >= 0) //while not end of file
        
            line = input.ReadLine();
            //perform operation on line
            outputWriter.writeline(line);
        
        input.Close();
    

操作_2的代码:

   private void Operation_2(TextReader input, ref MemoryStream output)
    
        input.Seek(0, SeekOrigin.Begin); //reset stream to start of file
        TextReader inputReader = new StreamReader(input);
        TextWriter outputWriter = new StreamWriter(output);
        String line;

        while (inputReader.Peek() >= 0) //while not end of file
        
            line = inputReader.ReadLine();
            //perform operation on line
            outputWriter.writeline(line);
        
        inputReader.Close();
    

操作_3的代码:

    private void operation_3(MemoryStream input, TextWriter output)
    
       input.Seek(0, SeekOrigin.Begin);  //reset stream to start of file
       TextReader inputReader = new StreamReader(input);
       String line;

       while (inputReader.Peek() >= 0) //while not end of file
       
            line = inputReader.ReadLine();
            //perform operation on line
            output.writeline(line);
       
       inputReader.Close();
       output.Close();
    

现在的问题是我得到的结果与将每个中间结果存储到硬盘上的物理 txt 文件并使用该文件进行下一个操作不同。几行,文件的结尾不见了。

此外,这似乎不是一种非常干净和通用的方式。

所以我的问题是;为什么将 MemoryStream 用于中间结果时我的结果会有所不同,是否有更清洁、更灵活的方法呢? (如果可以选择是否要保存中间结果,我想努力解决)。

【问题讨论】:

【参考方案1】: 它们是不同的,因为您忘记冲洗您的作家。 不需要ref 修饰符,因为您不会在操作中创建新的MemoryStream

这样你的方法会更干净一些:

private void Operation_1(TextReader input, Stream output)

    TextWriter outputWriter = new StreamWriter(output);
    String line;

    outputWriter.Write(input.ReadToEnd());
    outputWriter.Flush();

    input.Close();


private void Operation_2(Stream input, Stream output)

    input.Seek(0, SeekOrigin.Begin); //reset stream to start of file
    TextReader inputReader = new StreamReader(input);
    TextWriter outputWriter = new StreamWriter(output);

    outputWriter.Write(inputReader.ReadToEnd());
    outputWriter.Flush();
    inputReader.Close();

操作_3的代码:

private void operation_3(Stream input, TextWriter output)

   input.Seek(0, SeekOrigin.Begin);  //reset stream to start of file
   TextReader inputReader = new StreamReader(input);

   output.Write(inputReader.ReadToEnd());

   inputReader.Close();
   output.Flush();
   output.Close();

【讨论】:

啊,确实,.flush() 工作了 :D 现在的输出与只有 TextWriters 的代码相同 :) 所以不可能只使用一个 MemoryStream? 确实有可能。但是很难告诉你如何去做,因为你的代码毫无意义。 Operation_2 采用两个 MemoryStream,只是将输入的内容复制到输出流。如果你只想使用一个 MemoryStream,这个方法可以完全跳过。 好吧,我实际上对输入行进行了相当多的修改,但我忽略了那部分,因为我认为这与我的问题无关,只会掩盖手头的真正问题。无论如何,对于 Operation_2,这意味着我将从流中读取一行,对其进行更改,然后将其写回同一流。当我这样说时,这似乎是不可能的 至少,它让事情变得很多变得更加困难。我建议,在这种情况下,您只需坚持使用两个单独的流。【参考方案2】:

我有一个更改您的代码的建议。也许您可以使用 IEnumerable 而不是使用 Streams 和 TextReaders。

请参阅下面的示例(这只是一个示例。不包括错误处理以保持示例简单。)

上一个操作的结果作为参数提供给下一个。因此,它的执行如下 Operation3(Operation2(Operation1)))。

下面的第一个示例是逐行读取和更改文件中包含的行 下面的第二个示例是读取整个文件并更改为下一个操作提供所有行的行(lines.ToArray() 读取整个文件)。

对于流,您始终必须注意它们在所需时间被正确处理(例如,StreamReader 在 StreamReader 被处理时默认关闭内部流)。

using System;
using System.Collections.Generic;
using System.IO;
using System.Globalization;
using System.Linq;

namespace ConsoleApplication2

    class Program
    
        static void Main()
        
            //line per line...
            File.WriteAllLines
                (
                    @"C:\temp\output.txt",
                    ChangeLines(File.ReadLines(@"C:\temp\input.txt"),
                                line =>
                                LineOperation3
                                    (
                                        LineOperation2
                                            (
                                                LineOperation1(line)
                                            )
                                    )
                        )
                );

            //lines per lines...
            File.WriteAllLines
               (
                   @"C:\temp\output2WithCount.txt",
                   ChangeLines(File.ReadLines(@"C:\temp\input.txt"),
                               lines =>
                                        LinesCountOperation
                                        (
                                            LinesCountOperation
                                            (
                                                LinesCountOperation(lines,LineOperation1),
                                                LineOperation2
                                            )
                                            , LineOperation3
                                          )
                       )
               );
        

        private static IEnumerable<string> ChangeLines(IEnumerable<string> lines, Func<string, string> lineFunc)
        
            foreach (var line in lines)
            
                yield return lineFunc(line);
            
        

        private static IEnumerable<string> ChangeLines(IEnumerable<string> lines, Func<IEnumerable<string>, IEnumerable<string>> linesFunc)
        
            foreach(var changedLine in linesFunc(lines))
            
                if (changedLine != null)
                
                    yield return changedLine;
                
            
        

        private static IEnumerable<string> LinesCountOperation(IEnumerable<string> lines, Func<string, string> lineFunc)
        
            var readAllLines = lines.ToArray();
            var linesCount = readAllLines.Count();

            foreach (var line in readAllLines)
            
                var changedLine = lineFunc(line);
                if (changedLine == null)
                
                    continue;
                
                yield return string.Format(CultureInfo.InvariantCulture, "0-1", linesCount, changedLine);
            
        

        private static string LineOperation1(string line)
        
            return string.Format(CultureInfo.InvariantCulture, "01", line, "1");
        

        private static string LineOperation2(string line)
        
            return string.Format(CultureInfo.InvariantCulture, "01", line, "2");
        

        private static string LineOperation3(string line)
        
            return string.Format(CultureInfo.InvariantCulture, "01", line, "3");
        
    

【讨论】:

以上是关于使用一个内存流进行多个文件搜索迭代的主要内容,如果未能解决你的问题,请参考以下文章

for循环中的多个选择字符串以分隔文件

一个文件汇集搜索系统(NiFi + ELK)

Java中排序(内存映射?)文件中的二进制搜索

173. 二叉搜索树迭代器

使用 VSTS 进行负载测试。从 CSV 文件中搜索,然后单击第一个元素

使用 FFMPEG 将可搜索的 AAC 音频流写入 MP4 文件