问题(请查看编辑以了解更多信息)
我有一个大约包含1500个字符串的列表,对于这些字符串中的每一个,我都需要检查目录(包括子目录)中是否有任何文件包含该字符串(总共约有4000个文件)。
代码
我现在有以下两个变量:
原始代码
foreach(var str in stringList)
{
allFiles.Any(f => File.ReadAllText(f).Contains(str));
}
第二种方法(使用ReadLines而不是ReadAllText,正如VladL在这个问题中建议的那样)
foreach(var string in stringList)
{
allFiles.SelectMany(File.ReadLines).Any(line => line.Contains(str));
}
我只测试了原始版本的完整程序执行时间,用了21分钟才完成。然后我测试了单个语句(检查是否包含1个字符串在任何文件中),搜索一个我知道它不包含的字符串来检查最坏情况,这是我的计时结果(每次执行3次):
原版: 1285, 1369, 1336 毫秒
第二种变体: 2718, 2804, 2831 毫秒
我还尝试将原始语句中的ReadAllText替换为ReadAllLines(仅更改此处),但没有改变性能。
问题
有没有更快的方式来检查一个字符串是否包含在任何文件中(大量的大文件)?
编辑
我承认我没有表达清楚,我有一个csv文件列表,然后遍历这些文件并遍历每个文件的每一行(忽略第一行)。对于每一行,我将其与某些字段组合成一个字符串,然后查找是否有任何文件包含该字符串。
foreach(var csvFile in csvFiles)
{
var lines = File.ReadAllLines(csvFile);
foreach(var line in lines)
{
if (IsHeader(line)) continue;
var str = ComposeString(line);
var bool = allFiles.Any(f => File.ReadAllText(f).Contains(str));
// do stuff with the line and bool
}
}
Edit 2
public void ExecuteAhoCorasick()
{
var table = CreateDataTable();
var allFiles = GetAllFiles();
var csvFiles = GetCsvFiles();
var resList = new List<string>();
foreach(var csvFile in csvFiles)
{
if (file.Contains("ValueList_")) continue;
var lines = File.ReadAllLines(file);
foreach (var line in lines)
{
if (line == HeaderLine) continue;
var res = line.Split(';');
if (res.Length <= 7) continue;
var resPath = $"{res[0]}.{res[1]}.{res[2]}".Trim('.');
resList.Add(resPath);
var row = table.NewRow();
row[0] = res[0]; // Group
row[1] = res[1]; // Type
row[2] = res[2]; // Key
row[3] = res[3]; // Global
row[4] = res[4]; // De
row[5] = res[5]; // Fr
row[6] = res[6]; // It
row[7] = res[7]; // En
row[8] = resPath; // Resource Path
row[9] = false;
row[10] = ""; // Comment
row[11] = file; // File Path
table.Rows.Add(row);
}
}
var foundRes = new List<string>();
foreach (var file in allFiles)
{
// var chars = File.ReadLines(file).SelectMany(line => line);
var text = File.ReadAllText(file);
var trie = new Trie();
trie.Add(resList);
foundRes.AddRange(trie.Find(text));
// foundRes.AddRange(trie.Find(chars));
}
// update row[9] to true foreach res in foundRes
}
trie.Add()
之后添加trie.Build()
。另外请注意,您可以将trie的初始化移动到循环之前,这样您只需要在循环中使用trie.Find()
本身即可。这也应该会减少开销。(换句话说,将var trie = new Trie(); trie.Add(resList); trie.Build();
移到循环之前。) - Matthew Watson