1

我正在从第三方应用程序解析一个合理大小的日志文件(最大 50Mb,此时它会包装),以检测在指定时间范围内发生的 KEY_STRING。此日志文件中的典型条目可能如下所示

DEBUG 2013-10-11@14:23:49 [PID] - Product.Version.Module
(Param 1=blahblah Param2=blahblah Param3 =blahblah
Method=funtionname)
String that we usually don't care about but may be KEY_STRING  

条目以空行分隔(\r\n 在条目末尾,然后在下一个条目开始之前 \r\n)

这是针对特定于 Windows 的实现,因此不需要是可移植的,并且可以是 C/C++/Win32

逐行读取此内容将非常耗时,但其好处是能够解析时间戳并检查条目是否在给定时间范围内,然后再检查条目中是否存在任何 KEY_STRING。如果我按块读取文件,我可能会找到一个 KEY_STRING,但该块没有较早的时间戳,或者块边界甚至可能位于 KEY_STRING 的中间。将整个文件读入内存并解析它不是一个选项,因为它要成为其中一部分的应用程序当前占用的空间相对较小,因此不能证明仅为了解析文件(即使是暂时的)将其增加约 10 倍是合理的. 有没有办法可以通过分隔块(特别是“\r\n\r\n”)读取文件?还是有我没有想到的另一种/更好的方法?

对此的任何帮助将不胜感激!

4

3 回答 3

0
  1. 确保你不能使用内存,也许你有点太“偏执”了?过早的优化等等。
  2. 逐行阅读(因为这样更容易分隔条目),但使用缓冲读取包装行阅读,一次阅读尽可能多的内容,可能是 1 MB。这最大限度地减少了磁盘 I/O,这通常对性能有好处。
于 2013-10-14T14:38:45.353 回答
0

假设(通常情况下)文件中的所有条目都按时间排序,您应该能够使用二进制搜索的变体来找到正确的起点和终点,然后解析其间的数据。

基本思想是寻找文件的中间,然后读取几行,直到找到以“DEBUG”开头的一行,然后读取时间戳。如果它早于您关心的时间,请向前寻找 3/4标记。如果晚于您关心的时间,请回溯到 1/4。标记。重复基本想法,直到找到开始。然后在结束时间做同样的事情。

一旦您正在寻找的数量下降到某个阈值(例如,64K)以下,寻找到 64K 对齐块的开头可能会更快,并且从那里继续阅读而不是进行任何更多的寻找。

另一种需要考虑的可能性是,您是否可以在后台做一些工作以在文件被修改时为其建立索引,然后在您实际需要结果时使用该索引。索引将(例如)在每个条目写入后立即读取其时间戳(例如,ReadDirectoryChangesW在日志文件被修改时被告知)。它将文本时间戳转换为例如 time_t,然后在索引中存储一个条目,给出 time_t 和该条目的文件偏移量。这应该足够小(对于 50 兆字节的日志文件,可能低于 1 兆字节),以便完全在内存中使用它。

于 2013-10-14T14:43:49.557 回答
0

一种可能的解决方案是使用内存映射文件。除了玩具应用之外,我个人从未将它们用于任何用途,但知道它背后的一些理论。

从本质上讲,它们提供了一种访问文件内容的方式,就好像它们是内存一样,我相信其行为方式与虚拟内存类似,因此所需的部分将根据需要分页,并在某些时候分页(你应该阅读文档以制定背后的规则)。

在伪代码中(因为我们都喜欢伪代码),您可以按照以下方式进行操作:

HANDLE file = CreateFile(...);
HANDLE file_map = CreateFileMapping(file, 0, PAGE_READONLY, 0, 0, ...);
LPVOID mem = MapViewOfFile(file_map, FILE_MAP_READ, 0, 0, 0);

// at this point you can use mem to access data in the mapped part of the file...
// for your code, you would perform parsing as if you'd read the file into RAM.

// when you're done, unmap and close the file:
UnmapViewOfFile(mem);
CloseHandle(file_map);
CloseHandle(file);

我现在很抱歉没有给出最优秀的建议,而是鼓励进一步阅读- Windows 提供了很多功能来处理你的记忆,而且它最值得一读。

于 2013-10-14T16:11:21.597 回答