在多个文件中搜索多个字符串

6
我是一位能翻译文本的助手。

我有一个包含21000个字符串(每行一个)和500MB其他文本文件(主要是源代码)的文本文件。对于每个字符串,我需要确定它是否包含在任何这些文件中。我编写了一个程序来完成这项工作,但其性能非常差(它需要几天时间才能完成,而我需要在5-6小时内完成任务)。

我使用C#、Visual Studio 2010进行编写。

关于我的问题,我有几个问题:
a)哪种方法更好?

foreach(string s in StringsToSearch)
{
    //scan all files and break when string is found
}

或者

foreach(string f in Files)
{
    //search that file for each string that is not already found
}
b) 扫描一个文件一行一行地进行,这样更好吗?
StreamReader r = new StreamReader(file);
while(!r.EndOfStream)
{
    string s = r.ReadLine();
    //... if(s.Contains(xxx));
}

或者

StreamReader r = new StreamReader(file);
string s = r.ReadToEnd();
//if(s.Contains(xxx));

c) 使用线程会提高性能吗?如何实现?
d) 是否有软件可以代替手写代码实现线程?


你必须编写程序吗?Windows内置了findstr。您可以使用for循环并行搜索这些其他文件。 - Preet Sangha
绝对不是正确/完整的答案,但是不要为每个字符串加载所有(500MB!)文件。一旦将文件(部分)加载到内存中,请执行所有操作。 - Bertvan
我本意是逐个加载整个文件,而不是一次性加载500MB的文件。 - Ichibann
你使用的是哪个操作系统?如果你使用的是Win7,你可以自动搜索文件,而且速度相当快。 - kyndigs
5个回答

6
如果您只想知道字符串是否被找到或未找到,并且不需要进行任何进一步的处理,那么我建议您使用 grep。grep非常快速,并专门设计用于解决这种类型的问题。
grep -f strings-file other-files...

这应该就可以解决问题了。我相信在某个地方有Windows的实现,最坏情况下,Cygwin也会有它。

编辑:这回答了问题d)


是的,尽管有[C#]标签,但这可能是最好的方法。 - H H
我不熟悉grep以及它的工作原理,能否帮我介绍一下如何使用呢? - Ichibann
Grep是*nix系统中非常常见的工具。有很多文档可以参考,所以一定会有一个好的教程。建议的命令是在“strings-file”中查找所有字符串,并在“other-files”中查找匹配行并打印出来。有许多选项可用于更改输出内容以满足您的需求。 - Cameron Skinner

4

您希望最小化文件I/O,因此您的第一个想法非常糟糕,因为您将打开“其他”文件多达21,000次。您应该使用基于第二个想法(a1)的方法。当这些其他文件不是特别大时,可以使用readAllText一次性将它们加载到内存中。

List<string> keys = ...;    // load all strings

foreach(string f in Files)
{
    //search for each string that is not already found
    string text = System.IO.File.ReadAllText(f);  //easy version of ReadToEnd


    // brute force
    foreach(string key in keyes)
    {
        if (text.IndexOf(key) >= 0) ....
    }

}

暴力算法部分可以改进,但我认为您会发现它可接受。

如果(text.IndexOf(key)>=0)比if(text.Contains(key))更快吗? - Ichibann
@Ichi:不,我会期望它们的速度是相同的。 - H H

2
  1. 在a)和b)中,第二个选项更为高效。
  2. 线程可能不会提高性能,因为每个线程都会从磁盘读取文件,所以您的磁盘会成为瓶颈。
  3. 很抱歉,我对您的目的没有相关软件的了解。

线程代码片段

      foreach (FileInfo file in FileList)
      {
         Thread t  = new Thread(new ParameterizedThreadStart(ProcessFileData));
         t.Start(file.FullName);  
       }//where processFileData is the method that process the files

常规I/O指南

以下是一些基本建议,可减少程序的I/O活动,从而提高其性能。与所有建议一样,重要的是在优化代码之前和之后测量其性能,以确保它确实变得更快。

  1. 最小化执行文件操作的数量
  2. 将多个小的I/O传输组合成一个大的传输。单次写入八页比八个单独的单页写入更快,主要是因为它允许硬盘在磁盘表面上一次性写入数据。了解更多信息,请参见
  3. 执行顺序读取,而不是查找和读取小块数据。内核透明地聚集I/O操作,使顺序读取速度更快。
  4. 在写入数据之前避免跳过空文件。系统必须将零写入介于空间中以填补空白。有关更多信息,请参见读取通常比写入数据更便宜。
  5. 推迟任何I/O操作,直到您的应用程序实际需要数据为止。
  6. 使用首选项系统仅捕获用户首选项(例如窗口位置和视图设置),而不是可以廉价重新计算的数据。
  7. 不要假设将文件数据缓存到内存中会加快应用程序的速度。将文件数据存储在内存中可以提高速度,直到该内存被交换出到磁盘上,此时您再次访问磁盘就要付出代价。努力找到从磁盘读取和缓存到内存之间的适当平衡点

2

2
搜索必须实时进行吗?即对当前的 500 MB 文本进行实时搜索?我之所以问这个问题,是因为你可以在文本文件上构建搜索索引并执行搜索。这样会更快...可以看看 Lucene。

Lucene.Net

将C#和Lucene用于索引和搜索

不需要实时搜索,这只是一次性任务。做完就可以忘记它了 :P - Ichibann
然后使用Lucene(我没有使用Windows搜索SDK)构建完整的搜索索引,并针对其执行查找...我以前用过Lucene...它很快! - zam6ak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接