【问题标题】:Search a large file for data in C/C++在 C/C++ 中搜索大文件中的数据
【发布时间】:2010-02-05 20:34:41
【问题描述】:

我有一个具有这种格式的日志文件:

DATE-TIME ### attribute1 ### attribute2 ###attribute3 

我必须在此日志文件中搜索输入属性(从命令行输入)并输出与输入属性匹配的行。
一种天真的方法可能是这样的:

逐行扫描整个文件 搜索属性 如果找到则打印,否则忽略。

这种方法很慢,因为它需要 O(n) 次比较,其中 n 是可能非常大的行数。
另一种方法可能是使用哈希表,但可能无法为大文件保留这样的内存哈希表。
那么,最佳可行的解决方案是什么?我怎样才能索引整个文件的各种属性?

编辑:
日志文件可能有大约 100K 行,与 linux 上的系统日志文件差不多。 在一次调用中,用户可能会搜索多个属性,直到像交互式控制台一样完成第一个属性的搜索后才知道。

谢谢,

【问题讨论】:

  • 多少行,多少字节的数据?每次调用程序是否只有一次搜索,还是可以使用内存索引进行多次搜索?您可以对数据做出任何假设吗?您可以利用任何空间位置来索引文件中最常用的部分?
  • 可能超过 100K 行,和 linux(var/log/messages) 的系统日志文件差不多。在一次调用中,可能会有不止一个搜索,但在不同的属性上。
  • 缓慢、幼稚、未索引/未散列的版本需要多长时间?如果它的速度足以跟上交互式用户的步伐,则不必担心对其进行优化,除非您要扩展到数千或数百个用户。
  • 仅仅 100K 行?对于grep 来说,这看起来不错,或者说是 NIH 综合症的可怕案例。

标签: c++ c file indexing search


【解决方案1】:

如果你只搜索一次,你不会比 O(n) 做得更好。

如果哈希索引太大而无法放入内存,请使用磁盘上的哈希,例如 dbmgdbm

编辑:我想指出,KeithB 建议的 Berkeley DB 工具在磁盘哈希上属于这一类。 Berkeley DB 不是使用 SQL 的关系数据库。

【讨论】:

    【解决方案2】:

    您可以使用 Berkley DB 来索引此文件。基本上,遍历整个文件一次,对于找到的每个属性,存储该属性的文件位置。 Berkley DB 使用高效的 B-Tree 实现,不需要将整个索引存储在内存中,只需将需要的部分存储在内存中。 我以前做过,而且很成功。

    【讨论】:

    • 这是一些学习过程的一部分,我认为使用数据库会破坏这一点。谢谢你的回答。
    • 请记住,Berkley DB 不是典型的关系数据库。它基本上是一种存储键值对的方法。您(至少上次我看,那是几年前)仅限于查找键和遍历键。它可能有用,具体取决于您要学习的内容。
    • 我是这么说的,因为 BerkelyDB 的第一个链接将我带到了 oracle 的站点
    【解决方案3】:

    您可以通过仅在其中存储哈希值和文件偏移量来减小哈希表的大小。如果属性只有一个固定的、相对较少的值,那么您更有可能能够将整个哈希表放入内存中。您为属性的每个可能值分配一个 id,然后为每个 id 值存储一个大的文件偏移列表。

    当然,哈希表只有在程序的同一次运行中进行多次不同的搜索时才有用。

    显而易见的解决方案是将数据填充到数据库中,但我认为 OP 足够聪明,已经意识到这一点,并且有其他原因专门请求非数据库解决方案来解决问题。

    【讨论】:

      【解决方案4】:

      听起来像是数据库系统的工作。

      我怎样才能索引整个文件的各种属性?

      您真的无法在后台实施数据库解决方案。您最好的选择可能是一些离线搜索算法和维护索引文件。

      您还可以找到感兴趣的Map-Reduce

      【讨论】:

        【解决方案5】:

        没有直接搜索可变长度行的文本文件的有效方法。最有效的方法需要开销来将数据变异为更适合更有效的搜索方法的形式。对于不频繁的搜索,这种开销可能不值得。

        不经常搜索

        如果文件只搜索一次或不经常搜索,我建议逐行搜索。其他方法可能会浪费时间设置数据以进行更快的搜索。例如,使用您的程序查找包含一个或多个属性的第一行。

        频繁搜索

        程序需要多次搜索文件。在这种情况下,您可能需要设置一些数据结构以便更好地搜索。一种技术是创建索引文件。这些文件包含属性的文件位置,按属性排序。 [属性][第一次出现][第二次出现]之类的东西。

        另一种选择是让您的程序将文件转换为更好的工具可以使用的格式,例如电子表格或数据库。一个例子是逗号分隔值,或者某些工具喜欢用“|”分隔值。

        更换发电机

        然而,生成日志文件的程序可以更改为生成电子表格或数据库友好的日志文件。我用嵌入式系统做到了这一点。我将数据输入电子表格并使用电子表格函数来分析数据。编写程序来搜索(和分析)日志文件要容易得多。

        【讨论】:

          【解决方案6】:

          在单独的线程中进行搜索,这可能需要一段时间,但我认为在这种情况下,构建然后搜索哈希表的权衡是不值得的。

          如果用户反复搜索不同的属性,可能值得,但我对此表示怀疑。

          对于搜索尝试boost::regexQRegExp

          对于仅搜索日志文件,我认为您可能使用数据库或哈希表对其进行了过度设计。

          【讨论】:

            【解决方案7】:

            我会使用 b-tree 或哈希表来构建索引。我会将索引存储在磁盘上。如果您控制对此文件的更新,您还可以在文件更新时更新索引。如果您无法控制更新,则使用文件的哈希值来确定是否需要重新生成。

            考虑到这一点,我意识到我通常在命令行上使用 grep 搜索 600k+ 行 (100M) 的日志文件。它相当快。所以,除非您的文件有更多数据,否则我认为 100k 不是问题。

            如果日志文件变大,您可能还想考虑使用日志轮换。

            【讨论】:

              【解决方案8】:

              您可以尝试对文件进行简单的线性搜索,但启动一个工作线程来进行搜索。这样,您可以生成多个工作线程并在文件中的不同点启动它们。在具有多个处理器/内核的机器上,与普通的单线程线性搜索相比,这种并行性应该会产生性能提升。

              进行搜索后,您可能希望将输入参数和搜索结果存储在数据文件中。这样,如果以后重复搜索,您可以使用此缓存数据,而只需搜索自上次搜索后修改的文件部分。

              【讨论】:

                【解决方案9】:

                别担心。扫描一下就行了。

                说真的。你说这个文件有 100K 行,这实际上与我正在输入的计算机上的 /var/log/messages 几乎完全相同。嗯,这是一个上网本,即按照现代标准非常慢 - 但是我的 /var/log/messages 的简单 grep 非常快,以至于它与将结果打印到屏幕上所花费的时间相形见绌。

                所以我真的不认为你有什么可担心的——特别是如果这是一个交互式进程,它只在人类需要时运行搜索,而不是某个可能在后台不断搜索事物的守护进程。您很可能已经浪费了更多的时间来担心这个问题,而不是通过实施索引来节省!

                【讨论】:

                  【解决方案10】:

                  来吧,认真的伙计们。日志文件的数据库?

                  日志文件始终或至少每天都在更改。因此,您可能真正想要的是进行某种日志文件轮换,其中有许多预制和失败,如果您了解一点 perl,则可以在几个小时内完成。您也可能真的不需要 C++。它只会使开发时间变慢,最终结果更加错误。

                  【讨论】:

                  • 我知道 Perl 是最适合文本处理的,而且我知道的 perl 足以胜任这项工作,但是两种情况下的算法都是一样的。所以差别不大。
                  猜你喜欢
                  • 1970-01-01
                  • 2020-08-06
                  • 2018-12-10
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-07-10
                  • 1970-01-01
                  相关资源
                  最近更新 更多