一次一行从多个文件中读取特定行的最快方法

本文关键字:读取 方法 一次 一行 文件 | 更新日期: 2023-09-27 18:35:49

我有一个基于文本的数据库,它表示日志,按时间戳排序。出于测试目的,我的数据库大约有 10,000 行,但这个数字可能更大。它的格式是:

primary_key、source_file line_num

1, cpu.txt, 2
2, ram.txt, 3 3, cpu.txt, 3

我查询数据库,当我读取结果时,我想将实际数据添加到一个字符串中,然后我可以显示该字符串。上面示例中的实际数据是来自 cpu.txt 的第 2 行的内容,然后是来自 ram.txt 等的第 3 行的内容。行内容可能很长。

一个重要的注意事项是,每个文件的行号都是按顺序排列的。也就是说,下次我在数据库中遇到cpu.txt条目时,它将以行4作为行号。但是,我可能会在来自 ram.txt、硬盘驱动器.txt图形等的数千个其他条目之后看到 cpu.txt 条目.txt。

我考虑过使用以下代码:

StringBuilder odbcResults = new StringBuilder();
OdbcDataReader dbReader = com.ExecuteReader();  // query database
while (dbReader.Read())
{
   string fileName = dbReader[1].ToString(); // source file
   int fileLineNum = int.Parse(dbReader[2].ToString());  // line number in source file
   odbcResults.Append(File.ReadLines(fileName).Skip(fileLineNum).First());
}

但是,File.ReadLines()不想在每次迭代后处理其TextReader吗?效率不是很高?

我也有这个想法,为我需要在字典中读取的每个文件保留一个 StreamReader:

Dictionary<string, StreamReader> fileReaders = new Dictionary<string, StreamReader>();
StringBuilder odbcResults = new StringBuilder();
OdbcDataReader dbReader = com.ExecuteReader();
while (dbReader.Read())
{
   string fileName = dbReader[1].ToString(); // source file
   int fileLineNum = int.Parse(dbReader[2].ToString());  // line number in source file
   if (!fileReaders.ContainsKey(fileName))
   {
      fileReaders.Add(fileName, new StreamReader(fileName));
   }
   StreamReader fileReader = fileReaders[fileName];
   // don't have to worry about positioning? Lines consumed consecutively
   odbcResults.Append(fileReader.ReadLine());
}
// can't forget to properly Close() and Dispose() of all fileReaders

您是否同意上述任何示例,或者有更好的方法吗?
对于第二个示例,我假设 StreamReader 会记住它的最后一个位置 - 我相信这保存在 BaseStream 中。

我已经阅读了如何读取文本文件中的指定行?,在特定行读取文本文件,StreamReader 和查找(第一个答案提供了指向具有定位功能的自定义 StreamReader 类的链接,但我只知道我需要打开的行号,而不是偏移量),但我认为他们没有具体回答我的问题。

一次一行从多个文件中读取特定行的最快方法

如果你能保证你的行引用在文件中是严格连续的(即,在你请求第 n 行之后,你总是要求第 n+1 行),那么你选择保留StreamReader实例的字典看起来是个好主意。

如果您可能要求第 n 行,然后是第 n+x 行(其中 x 是某个正数>= 1),那么我会将该StreamReader包装在一个对象中,该对象跟踪当前行号并具有返回请求的行号的方法GetLine(int lineNo)。假设请求的行号大于当前行号(不允许向后读取)。

您不必担心定位。这是为您处理的,因为您正在按顺序阅读。

听起来您希望在内存中(用于在文本框中显示)用户选择的所有内容 - 因此无论如何,这是可行的自然边界。我建议采用以下方法:

    将所有
  • 匹配的元数据(即在用户指定的时间范围内)从数据库中读取到列表中。保留一组我们需要读取的文件。
  • 创建一个与列表大小相同的新数组 - 这将保存最终数据
  • 一次
  • 浏览一个所需的文件:
    • 打开文件,记住我们在第 0 行
    • 循环访问元数据列表。对于与我们当前打开的文件匹配的每个条目,向前阅读正确的行,并填充与我们正在查看的列表条目对应的最终数据数组元素。我们只需要向前阅读,因为我们仍然按照时间戳顺序进行。
    • 关闭文件

此时,"最终数据数组"应已完全填充。您一次只需要打开一个文件,并且永远不需要读取整个文件。我认为这比拥有打开文件的字典更简单 - 除此之外,这意味着您可以为每个文件使用 using 语句,而不必手动处理所有关闭。

这确实意味着一次将所有数据库元数据条目放在内存中,但大概每个元数据条目都小于结果数据,无论如何,为了向用户显示结果,您最终需要在内存中

拥有结果。

即使您将多次遍历数据库元数据条目,这些都将在内存中发生。与文件系统或数据库的 IO 相比,它应该是微不足道的。

另一种方法是在读取元数据条目时按文件名对元数据条目进行分组,将索引作为元数据条目的一部分进行维护。