c#快速搜索大型文本文件



我已经浏览了一篇与视图相关的文章,但还没有找到适合我查询的解决方案。在一个大的纯文本文件(~150MB, ~ 1,800.000行)中,我想快速找到使用c#具有某些特性的特定行。

每行有132个字符,每个字符有一个区域-,一个节-,一个子代码和一个标识符。这四个特征的结合是独一无二的。根据节代码的不同,其他部分的确切位置可能不同。

实际上,我想用一个方法检索最多~50个元素,理想情况下需要不到1秒的时间。

到目前为止,我的代码可以工作,但是对于我的目的来说速度很慢(30个条目执行约29秒):

//icaoCode is always 2 char long
public static List<Waypoint> Retrieve(List<(string ident, string icaoCode, char sectionCode, char subSectionCode)> wpData)
{
List<Waypoint> result = new List<Waypoint>();
using StreamReader reader = new StreamReader(dataFile);
while (!reader.EndOfStream)
{
string data = reader.ReadLine();
if (data.Length != 132) continue;
foreach(var x in wpData)
{
int subsPos = (x.sectionCode, x.subSectionCode) switch
{
('P', 'N') => 5,
('P', _) => 12,
(_, _) => 5
};
if (data[4].Equals(x.sectionCode) && data[subsPos].Equals(x.subSectionCode))
{
//IsNdb() and others look at the sectionCode and subSectionCode to determine data type
if (IsNdb(data) && data[13..17].Trim() == x.ident && data[19..21] == x.icaoCode) result.Add(ArincHelper.LoadNdbEntry(data));
else if (IsVhf(data) && data[13..17].Trim() == x.ident && data[19..21] == x.icaoCode) result.Add(ArincHelper.LoadVhfEntry(data));
else if (IsTacan(data) && data[13..17].Trim() == x.ident) result.Add(ArincHelper.LoadTacanEntry(data));
else if (IsIls(data) && data[13..17].Trim() == x.ident && data[10..12] == x.icaoCode) result.Add(ArincHelper.LoadIlsEntry(data));
else if (IsAirport(data) && data[6..10] == x.ident && data[10..12] == x.icaoCode) result.Add(ArincHelper.LoadAirportEntry(data));
else if (IsRunway(data) && (data[6..10] + data[15..18].Trim()) == x.ident && data[10..12] == x.icaoCode) result.Add(ArincHelper.LoadRunwayEntryAsWaypoint(data));
else if (IsWaypoint(data) && data[13..18].Trim() == x.ident && data[19..21] == x.icaoCode) result.Add(ArincHelper.LoadWaypointEntry(data));
}
}
}
reader.Close();
return result;
}

IsNdb()和其他识别方法都是这样的:

private static bool IsNdb(string data) => (data[4], data[5]) == ('D', 'B') || (data[4], data[5]) == ('P', 'N');

一些示例数据行如下:

SEURPNEBBREB OP    EB004020HOM  N50561940E004353360                       E0010           WGEBRUSSELS                      169641609
SEURP EDDFEDAFRA     0FL100131Y N50015990E008341364E002300364250FFM ED05000          MWGE    FRANKFURT/MAIN                331502006
SEURD        CHA   ED011535VDHB N49551597E009022334CHA N49551597E009022334E0020005292  249WGECHARLIE                       867432005
SEURP LFFKLFCFK404 LF0    W F   N46262560W000480430                       E0000     WGE           FK404                    331071909

我希望避免将整个文件加载到内存中,因为这需要~400MB的RAM,尽管这当然是可能的。

提前感谢您的帮助。

编辑:当前的解决方案将此数据文件转换为SQLite数据库,然后使用该数据库。然而,这需要大约3小时的时间将文件转换为DB,这是我想避免的,因为数据文件会定期交换出来。这就是为什么我想给这个文本解析一个尝试。

正如@mjwills建议的那样,有更适合这项工作的工具,我将把这些数据保存在数据库中。如果我要尝试使您当前的代码更快,我会尝试以下方法。我会将数据块读入数组,并在并行for循环中处理这部分数据,当元素足够多时退出循环。下面是一些帮助您入门的伪代码。我写不出完整的代码,因为我没有object/file。

List<Waypoint> result = new List<Waypoint>();
var max = 1800000; //set the to the max rows in your file
var allLines = new string[max];
var dataFile = "";
using (StreamReader sr = File.OpenText(dataFile))
{
int x = 0;

while (!sr.EndOfStream)
{
allLines[x] = sr.ReadLine();
x += 1;
if (x % 5000 == 0)
{
var i = x - 5000;
Parallel.For(i, allLines.Length, x =>
{
//do your process here and exit if you have enough elements also set
//a flag to exit the while loop
});
}
//you would have to write some code to handle the last group of records that are less than 5k
}
}

您可以使用Gigantor,它应该快很多倍。Gigantor是一个c#库,用于对巨大文件进行快速正则表达式搜索。它可以作为源代码或nuget包获得。

Gigantor的搜索基准在大约3秒内搜索5gb,总共找到105160个匹配项。

您只需要将解析代码转换为正则表达式即可。

最新更新