0

我有一个表示日志的基于文本的数据库,按时间戳排序。出于测试目的,我的数据库大约有 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在数据库中遇到一个条目时,它将以 line4作为行号。但是,我可能仅在来自 ram.txt、harddrive.txt、graphics.txt 等的数千个其他条目之后才看到 cpu.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 类的链接,但我只知道我需要在的行号,而不是偏移量)但不认为他们具体回答我的问题。

4

2 回答 2

2

If you can guarantee that your line references are strictly sequential in the file (i.e. you always ask for line n+1 after you've asked for line n), then your option of keeping a dictionary of StreamReader instances looks like a good idea.

If you might ask for line n, then line n+x (where x is some positive number >= 1), then I'd wrap that StreamReader in an object that keeps track of the current line number and has a method GetLine(int lineNo) that will return the requested line number. Assuming that the requested line number is greater than the current line number (no reading backwards allowed).

You shouldn't have to worry about positioning. That's handled for you because you're reading sequentially.

于 2013-10-16T18:53:14.157 回答
1

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

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

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

这确实意味着一次将所有数据库元数据条目保存在内存中,但可能每个元数据条目都小于最终需要在内存中保存的结果数据,以便向用户显示结果。

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

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

于 2013-10-16T20:31:44.080 回答