簡體   English   中英

在多個文件中搜索文本的最快方法?

[英]Fastest way to search text in multiple files?

我需要在大約120個文本文件中找到一些文本,我想知道哪種方法是搜索文本的最佳和最快方法。 我應該讀取RichTextBox中的每個文件,然后使用其方法搜索文本嗎?還是應該將這些文件讀取為字符串變量,然后使用正則表達式進行搜索?

我認為,性能背后的主要因素是找到一種方法,從而無需遍歷已經測試過匹配的線路。 有什么方法可以一次找到文件中的所有匹配項? 有誰知道像Visual Studio一樣在文本文件中查找匹配項的方法? 它在大約800-1000毫秒內搜索了200個文本文件以進行匹配。 我認為它利用多個線程來完成此任務。

從您的描述(120個文件,70K-80K字,每個文件1-2 MB)來看,最好的方法似乎是一次讀取文件並建立可以搜索的索引。 我在下面提供了一個示例來說明如何完成此操作,但是如果您需要比查找精確術語或前綴術語更復雜的搜索詞匹配功能,那么這可能對您的作用有限。

如果您需要更復雜的文本搜索匹配(同時獲得良好的性能),我建議您研究一下專門為此目的而構建的出色的Lucene庫。

public struct WordLocation
{
    public WordLocation(string fileName, int lineNumber, int wordIndex)
    {
        FileName = fileName;
        LineNumber = lineNumber;
        WordIndex = wordIndex;
    }
    public readonly string FileName; // file containing the word.
    public readonly int LineNumber;  // line within the file.
    public readonly int WordIndex;   // index within the line.
}

public struct WordOccurrences
{
    private WordOccurrences(int nOccurrences, WordLocation[] locations)
    {
        NumberOfOccurrences = nOccurrences;
        Locations = locations;
    }

    public static readonly WordOccurrences None = new WordOccurrences(0, new WordLocation[0]);

    public static WordOccurrences FirstOccurrence(string fileName, int lineNumber, int wordIndex)
    {
        return new WordOccurrences(1, new [] { new WordLocation(fileName, lineNumber, wordIndex) });
    }

    public WordOccurances AddOccurrence(string fileName, int lineNumber, int wordIndex)
    {
        return new WordOccurrences(
            NumberOfOccurrences + 1, 
            Locations
                .Concat(
                    new [] { new WordLocation(fileName, lineNumber, wordIndex) })
                .ToArray());
    }

    public readonly int NumberOfOccurrences;
    public readonly WordLocation[] Locations;
}

public interface IWordIndexBuilder
{
    void AddWordOccurrence(string word, string fileName, int lineNumber, int wordIndex);
    IWordIndex Build();
}

public interface IWordIndex
{
    WordOccurrences Find(string word);
}

public static class BuilderExtensions
{
    public static IWordIndex BuildIndexFromFiles(this IWordIndexBuilder builder, IEnumerable<FileInfo> wordFiles)
    {
        var wordSeparators = new char[] {',', ' ', '\t', ';' /* etc */ };
        foreach (var file in wordFiles)
        {
            var lineNumber = 1;
            using (var reader = file.OpenText())
            {
                while (!reader.EndOfStream)
                {
                    var words = reader
                         .ReadLine() 
                         .Split(wordSeparators, StringSplitOptions.RemoveEmptyEntries)
                         .Select(f => f.Trim());

                    var wordIndex = 1;
                    foreach (var word in words)
                        builder.AddWordOccurrence(word, file.FullName, lineNumber, wordIndex++);

                    lineNumber++;
                }
            }
        }
        return builder.Build();
    }
}

然后,最簡單的索引實現(只能執行精確匹配查找)在內部使用字典:

public class DictionaryIndexBuilder : IIndexBuilder
{
    private Dictionary<string, WordOccurrences> _dict;

    private class DictionaryIndex : IWordIndex 
    {
        private readonly Dictionary<string, WordOccurrences> _dict;

        public DictionaryIndex(Dictionary<string, WordOccurrences> dict)
        {
            _dict = dict;
        }
        public WordOccurrences Find(string word)
        {
           WordOccurrences found;
           if (_dict.TryGetValue(word, out found);
               return found;
           return WordOccurrences.None;
        }
    }

    public DictionaryIndexBuilder(IEqualityComparer<string> comparer)
    {
        _dict = new Dictionary<string, WordOccurrences>(comparer);
    }
    public void AddWordOccurrence(string word, string fileName, int lineNumber, int wordIndex)
    {
        WordOccurrences current;
        if (!_dict.TryGetValue(word, out current))
            _dict[word] = WordOccurrences.FirstOccurrence(fileName, lineNumber, wordIndex);
        else
            _dict[word] = current.AddOccurrence(fileName, lineNumber, wordIndex);
    }
    public IWordIndex Build()
    {
        var dict = _dict;
        _dict = null;
        return new DictionaryIndex(dict);
    }
}

用法:

var builder = new DictionaryIndexBuilder(EqualityComparer<string>.Default);
var index = builder.BuildIndexFromFiles(myListOfFiles);
var matchSocks = index.Find("Socks");

如果您還想進行前綴查找,請實現使用排序字典的索引構建器/索引類(並更改IWordIndex.Find方法以返回多個匹配項,或者向接口添加新方法以查找部分/模式匹配項)。

如果要進行更復雜的查找,請選擇諸如Lucence之類的東西。

如果您在這里,我會怎么做:

1-我將所有文件路徑加載到字符串列表。

2-我將創建一個新列表來存儲與我的搜索詞匹配的文件路徑。

3-我將在文件列表中循環foreach並搜索我的術語,然后將匹配的文件添加到新列表中。

string searchTerm = "Some terms";
    string[] MyFilesList = Directory.GetFiles(@"c:\txtDirPath\", "*.txt");
    List<string> FoundedSearch=new List<string>();
    foreach (string filename in MyFilesList)
    {
        string textFile = File.ReadAllText(filename);
        if (textFile.Contains(searchTerm))
        {
            FoundedSearch.Add(filename);
        }
    }

那么您可以隨心所欲地處理List:FoundedSearch。

順便說說:

我不知道最佳答案,但是性能會非常好,直到800個文本文件(每個文件1000個單詞),您可以在此圖表中找到很好的性能

我假設您需要在每個文件中搜索相同的字符串。 您可以為每個搜索使用已compiled regex

string searchTerm = "searchWord";
Regex rx = new Regex(String.Format("\b{0}\b", searchTerm), RegexOptions.Compiled);
List<string> filePaths = new List<string>();

foreach (string filePath in filePaths)
{
   string allText = File.ReadAllText(filePath);
   var matches = rx.Matches(allText);             
   //rest of code
}

您必須對性能進行基准測試,但我想主要的瓶頸將是從磁盤打開和讀取文件。 如果事實如此,您可以查看“ 內存映射文件 ”。 或者,根據您最終想要做什么,一個專用的文本搜索器(例如Lucene.Net (如注釋中提到的I4V)可能更合適。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM