C#正则表达式性能非常慢
我对正则表达式的话题很陌生。我想用以下正则表达式解析日志文件:C#正则表达式性能非常慢,c#,regex,C#,Regex,我对正则表达式的话题很陌生。我想用以下正则表达式解析日志文件: (?<time>(.*?))[|](?<placeholder4>(.*?))[|](?<source>(.*?))[|](?<level>[1-3])[|](?<message>(.*?))[|][|][|](?<placeholder1>(.*?))[|][|](?<placeholder2>(.*?))[|](?<placeholder3
(?<time>(.*?))[|](?<placeholder4>(.*?))[|](?<source>(.*?))[|](?<level>[1-3])[|](?<message>(.*?))[|][|][|](?<placeholder1>(.*?))[|][|](?<placeholder2>(.*?))[|](?<placeholder3>(.*))
带有appr的日志文件。3000行需要很长的时间来解析它。你有没有一些加快表演的提示?谢谢你
更新:
我使用regex是因为我使用不同的日志文件,这些文件的结构不同,我使用它的方式是:
string[] fileContent = File.ReadAllLines(filePath);
Regex pattern = new Regex(LogFormat.GetLineRegex(logFileFormat));
foreach (var line in fileContent)
{
// Split log line
Match match = pattern.Match(line);
string logDate = match.Groups["time"].Value.Trim();
string logLevel = match.Groups["level"].Value.Trim();
// And so on...
}
解决方案:谢谢你的帮助。我用以下结果对其进行了测试:
1.)仅添加了RegexOptions。已编译:
从00:01:10.9611143到00:00:38.8928387
2.)使用Thomas Ayoub正则表达式
从00:00:38.8928387到00:00:06.3839097
3.)使用过的Wiktor Stribiżew regex
从00:00:06.3839097到00:00:03.2150095
如果您多次使用同一个正则表达式,请确保您编译它,以避免每次都重新创建正则表达式。这可以产生多个数量级
var regex = new Regex(".*", RegexOptions.Compiled);
下面的LinqPad代码显示了3种使用正则表达式的方法,从最快到最慢
regexFast
方法约需5秒,regexSlow
方法约需6秒,regexSlowest
约需50秒
void Main()
{
var sw = new Stopwatch();
var regex = @"(?<first>T[he]{2})\s*\w{5}.*";
// This is the fastest method.
sw.Restart();
var regexFast = new Regex(regex, RegexOptions.Compiled);
for (int i = 0; i < 9999999; i++)
{
regexFast.Match("The quick brown fox");
}
sw.Stop();
sw.ElapsedMilliseconds.Dump();
// This is a little slower - we didn't compile the regex so it has
// to do some extra work on each iteration.
sw.Restart();
var regexSlow = new Regex(regex);
for (int i = 0; i < 9999999; i++)
{
regexSlow.Match("The quick brown fox");
}
sw.Stop();
sw.ElapsedMilliseconds.Dump();
// This method is super slow - we create a new Regex each time, so
// we have to do *lots* of extra work.
sw.Restart();
for (int i = 0; i < 9999999; i++)
{
var regexSlowest = new Regex(regex);
regexSlowest.Match("The quick brown fox");
}
sw.Stop();
sw.ElapsedMilliseconds.Dump();
}
void Main()
{
var sw=新秒表();
var regex=@“(?T[he]{2})\s*\w{5}.*”;
//这是最快的方法。
sw.Restart();
var regexFast=newregex(Regex,RegexOptions.Compiled);
对于(int i=0;i<999999;i++)
{
regexFast.Match(“快速棕色狐狸”);
}
sw.Stop();
sw.elapsedmillesons.Dump();
//这有点慢-我们没有编译正则表达式,所以它有
//在每次迭代中做一些额外的工作。
sw.Restart();
var regexSlow=新正则表达式(Regex);
对于(int i=0;i<999999;i++)
{
regexSlow.Match(“快速棕色狐狸”);
}
sw.Stop();
sw.elapsedmillesons.Dump();
//这个方法非常慢-我们每次都创建一个新的正则表达式,所以
//我们必须做很多额外的工作。
sw.Restart();
对于(int i=0;i<999999;i++)
{
var regexSlowest=新正则表达式(Regex);
regexSlowest.Match(“快速棕色狐狸”);
}
sw.Stop();
sw.elapsedmillesons.Dump();
}
您的正则表达式可以优化为:
(?<time>([^|]*))[|](?<placeholder4>([^|]*))[|](?<source>([^|]*))[|](?<level>[1-3])[|](?<message>([^|]*))[|]{3}(?<placeholder1>([^|]*))[|][|](?<placeholder2>([^|]*))[|](?<placeholder3>([^|]*))
(?([|]*)[|][|](?([|]*)[|](?([|]*))[|](?[1-3])[|](?([|]*)[|]{3}(?([|]*)[|][|][|](?([|]*)[|])[|][|]]((?([|]*))[|])[|]
使用否定的char类而不是惰性量词。它减少了回溯。通过此更改,Regex101从316个步骤变为47个步骤。将它与RB的答案结合起来,您应该会很好让我将我的评论“转换”为答案,因为现在我知道您可以对regex性能做些什么了
,将所有*?
替换为[^ |]*
,并将所有重复的[|][|][|][|][|]
替换为[|]{3}
(或类似内容,具体取决于[|]
的数量。此外,不要使用嵌套的捕获组,这也会影响性能
var logFileFormat = @"(?<time>[^|]*)[|](?<placeholder4>[^|]*)[|](?<source>[^|]*)[|](?<level>[1-3])[|](?<message>[^|]*)[|]{3}(?<placeholder1>[^|]*)[|]{2}(?<placeholder2>[^|]*)[|](?<placeholder3>.*)";
为什么要使用正则表达式呢?似乎您可以使用
String.Split
来获取列数组。除非您确实需要选择这种类型的行(在特定列中包含数字),否则您确实可以使用String.Split()
。否则,将所有*?
替换为[^ |]*
,用[|][|][|][|][|]
替换[|]{3}也可以提高速度
。实际上,即使您需要检查某个特定列值是否为数字,您也可以使用非正则表达式代码进行检查……您还没有向我们展示如何实际匹配正则表达式-请添加相关代码。哦,是的,正则表达式对象(如果使用非静态方法)应该在循环之外创建。字符类[|]
似乎是一种获取一个字符的低效方法。正则表达式引擎可能会将其优化为单个显式字符,但可能不会。我会在整个过程中将它们替换为\\
。您也可以使用正则表达式缓存:perf应该类似于第二种方式编译的可能会有所帮助,正如RB所说,但是那些*?
>s才是真正的问题。我会在处理时去掉嵌套组。您也可以使用RegexOptions.ExplicitCapture来删除嵌套的捕获组。这很有意义,因为命名组将捕获相关的详细信息。
Regex pattern = new Regex(LogFormat.GetLineRegex(logFileFormat), RegexOptions.Compiled);