C#代码在一个非常大的文本文件中执行二进制搜索

时间:2010-04-13 15:13:55

标签: c# file-io binary-search

是否有可用于在非常大的文本文件中执行二进制搜索的库(可以是10GB)。

该文件是一种日志文件 - 每一行都以日期和时间开头。因此行是有序的。

6 个答案:

答案 0 :(得分:4)

由于线长不能保证长度相同,因此您需要某种形式的可识别线路分隔符,例如:回车或换行。

二进制搜索模式可以是您的传统算法。寻找文件的“中间”(按长度),向后搜索(逐字节)到您碰巧进入的行的开头,由行分隔符序列标识,读取该记录并进行比较。根据比较,向上或向下寻找(以字节为单位)并重复。

当您识别记录的起始索引时,请检查它是否与上次搜索相同。您可能会发现,当您拨入目标记录时,中途移动不会让您获得不同的记录。例如你有相邻的100字节和50字节的记录,所以跳入75字节总是会带你回到第一条记录的开头。如果发生这种情况,请在进行比较之前继续阅读下一条记录。

您应该会发现很快就能达到目标。

答案 1 :(得分:4)

我开始编写关于如何操作的伪代码,但我放弃了,因为它看起来似乎居高临下。你可能知道如何编写二进制搜索,它真的不复杂。

您无法在图书馆中找到它,原因有两个:

  1. 这不是真正的“二分搜索” - 线条大小不同,所以你需要调整算法(例如找文件的中间部分,然后寻找下一个“换行符”并认为它是“中间线” “)。
  2. 您的日期时间日志格式很可能是非标准的(好吧,它可能看起来像“标准”,但想一想......您可能会使用'[]'或其他东西将日期与日志消息分开,某事喜欢[10/02/2001 10:35:02]我的留言)。
  3. 总结 - 我认为您的需求过于具体且太简单,无法在自定义代码中实现,因为有人无法编写库:)

答案 2 :(得分:2)

您需要能够流式传输文件,但您还需要随机访问。我不确定你是如何做到这一点的,保证文件的每一行都包含相同的字节数。如果你有这个,你可以得到一个对象流,并使用Seek方法在文件中移动,然后从那里你可以通过读取构成一行的字节数来进行二进制搜索。但同样,这只有在行数相同的情况下才有效。否则,你会跳进线中间。

这样的东西
byte[] buffer = new byte[lineLength];
stream.Seek(lineLength * searchPosition, SeekOrigin.Begin);
stream.Read(buffer, 0, lineLength);
string line = Encoding.Default.GetString(buffer);

答案 3 :(得分:0)

在您为文件中的每个换行符保留内存中的Int64的约束下,这应该不会太糟糕。这实际上取决于文本行的平均长度,给定每行1000个字节(10,000,000,000 / 1000 * 4)= 40mb。非常大,但可能。

所以试试这个:

  1. 扫描文件并在List
  2. 中存储每个换行符的序号偏移量
  3. 二进制搜索List,使用自定义比较器扫描到文件偏移量并读取数据。

答案 4 :(得分:0)

如果您的文件是静态的(或很少更改)并且您必须针对它运行“足够”的查询,我相信最好的方法是创建“索引”文件:

  1. 扫描初始文件并获取文件的日期时间部分以及它们在原始文件中的位置(这就是为什么必须非常静态)将它们编码为一些如何(例如:unix time(full 10 digits)+纳秒(零填充4位数)和行位置(归零10位数字)。这样你就会有一致的“行”文件

  2. 对该文件进行二进制搜索(您可能需要有点创意才能实现范围搜索)并获取原始文件中的相关位置

  3. 直接从原始文件中读取,从给定位置开始/读取给定范围
  4. 你有使用O(log(n))运行时的范围搜索:)(并且你已经创建了原始数据库功能)

    毋庸置疑,如果文件数据文件“太频繁”更新,或者您没有针对索引文件运行“足够”的查询,那么您最终会花费更多时间来创建索引文件而不是保存查询文件。

    顺便说一句,使用此索引文件不需要对数据文件进行排序。由于日志文件往往仅附加和排序,您可以通过简单地创建仅保存数据文件中EOL标记位置(零填充10位数)的索引文件来加速整个过程 - 这样您就可以预先形成直接在数据文件上进行二进制搜索(使用索引文件以确定原始文件中的搜索位置),如果将行附加到日志文件,则只需将其EOL位置添加(追加)到索引文件即可。

答案 5 :(得分:-2)

List对象具有二进制搜索方法。

http://msdn.microsoft.com/en-us/library/w4e7fxsh%28VS.80%29.aspx