如何在C / C ++中随机访问文本文件?

时间:2012-08-27 18:47:38

标签: c++ c unix

我有一个大日志文件,其中记录按时间排序。每行都有时间。我需要在时间T1和时间T2之间找到所有记录(T1 <= T2)。我可以逐行扫描整个文件并找到T1的起始行,将其复制到缓冲区然后扫描下一行直到我到达结束时间T2。这可行,但效率不高。

我想知道我是否可以使用二进制搜索来定位时间T1和T2的行。但我不确定如何确定以下内容:

  1. 文件的中间行
  2. 如何确定我们应传递给lseek()的偏移量?
  3. 可以在文件上使用二进制搜索吗?

4 个答案:

答案 0 :(得分:1)

如果您有足够的地址空间,请考虑使用内存映射文件。它们往往是最简单,最有效的方法之一。使用boost :: iostreams :: mapped_file来实现可移植性。

答案 1 :(得分:1)

让我们假设,您的线条在平均长度附近都是合理的(意味着没有线占据日志的一半左右),这将使二元搜索变得可行。

接下来我还假设您将拥有以下功能:

//find the first start of a new log line after (or including) position start
//return the last position of the file if no start could be found
streampos findNextLineStart(ifstream &file, streampos start);
//extract the data as a timestamp from a line
int extractDate(ifstream &file, streampos lineStart);

通过这些功能,我们可以实现以下功能:

//find the position of the first line whose date is bigger than the given
streampos lower_bound(ifstream &file, int date)
{
  file.seekg(0, ios::end);
  streampos begin = 0, 
            end = file.tellg();
  while(begin < end)
  {
     streampos cur = (begin + end) / 2;
     streampos start = findNextLineStart(file, cur);
     //was a line start found?
     if(start < end)
     {
        int lineDate = extractDate(file, start);
        if(lineDate < date)
          begin = start;
        else
          end = start;
     }
     else
       //narrow the bound as no line was found
       end = cur;
  }
  return begin;
}

我不保证这个工作(在所有角落的情况下),但它草拟了整体实现。一个人会使用另一个函数upper_bound,你可以使用那些函数来开始和结束你的界限。

答案 2 :(得分:1)

首先,您可能不希望进行二进制搜索以查找范围中的最后一条记录。一旦找到T1,你就会线性地读取记录,直到找到一个超出所需范围的记录,所以你真的只需要找到该范围内的第一条记录。

此外,您不需要通过查找确切的第n / 2条记录来实现二进制搜索。如果您只是寻找两个当前边界之间的字节,找到下一个完整记录并从中更新边界,那么这应该没问题。

答案 3 :(得分:0)

你不需要中间线。相反,您可以使用中间的字符,然后一次向后移动一个字符,直到找到换行符;然后你知道你有当前行的开头。如果该行的时间戳在将来太远,那么您可以丢弃该行及其后的所有内容。如果它的时间戳过去太远,请丢弃它以及之前的所有内容。您可以重复此操作,直到找到所需的行。

这是标准的二进制搜索算法。你真的不需要二进制搜索中的中间线 - 它就足以拥有大约中间的东西。在某些极端情况下,某些线条比其他线条 更长,但可能会很慢。但通常它会足够快。