【问题标题】:A more efficient Regex or alternative?更有效的正则表达式或替代方法?
【发布时间】:2009-02-04 23:23:44
【问题描述】:

我有一个超过一百万行的文件。

 {<uri::rdfserver#null> <uri::d41d8cd98f00b204e9800998ecf8427e> <uri::TickerDailyPriceVolume> "693702"^^<xsd:long>}
 {<uri::rdfserver#null> <uri::d41d8cd98f00b204e9800998ecf8427e> <uri::TickerDailyPriceId> <uri::20fb8f7d-30ef-dd11-a78d-001f29e570a8>}

每一行都是一个语句。

struct Statement
    string C;
    string S;
    string P;
    string O;
    string T;

目前我在 while 循环中使用 TextReader 并使用正则表达式解析每一行:

Regex lineParse = new Regex(@"[^<|\""]*\w[^>\""]*", RegexOptions.Singleline | RegexOptions.Compiled);

执行此解析需要相当长的时间,我希望有人能指出更有效的解析策略。

有些行有 5 个匹配项,有些有 4 个。以下是每行的解析方式:

{<uri::rdfserver#null> <uri::d41d8cd98f00b204e9800998ecf8427e> <uri::TickerDailyPriceVolume> "693702"^^<xsd:long>}

Statement()
    C = uri::rdfserver#null
    S = uri::d41d8cd98f00b204e9800998ecf8427e
    P = uri::TickerDailyPriceVolume
    O = 693702
    T = xsd:long

{<uri::rdfserver#null> <uri::d41d8cd98f00b204e9800998ecf8427e> <uri::TickerDailyPriceId> <uri::20fb8f7d-30ef-dd11-a78d-001f29e570a8>}

Statement()
    C = uri::rdfserver#null
    S = uri::d41d8cd98f00b204e9800998ecf8427e
    P = uri::TickerDailyPriceId
    O = uri::20fb8f7d-30ef-dd11-a78d-001f29e570a8

来自 cmets 的其他信息:“我看到的性能不佳实际上是由于我在代码中设置了一个条件断点。没有那个断点,一切都会很快。如果有人有任何改进的想法我会感兴趣”-Eric Sc​​hoonover

【问题讨论】:

  • 我看到的性能不佳实际上是由于我在代码中设置了条件断点。没有那个断点,一切都很快。如果有人有任何改进的想法我会感兴趣:)
  • 您可能会将该信息添加到您的帖子中,这样人们就知道您不再追求速度了。
  • 我仍在寻找速度,只是我发布的正则表达式并不一定像我想象的那么慢。
  • 我很想知道您的方法与@sixlettervariables 提供的方法相比如何。我希望拆分方法明显更快。
  • 顺便说一下,在这个字符类 - [^

标签: c# .net regex


【解决方案1】:

最快(如下图)是简单的字符串拆分:

line.Split(new char[] { '{', '<', '>', '}', ' ', '^', '"' },
           StringSplitOptions.RemoveEmptyEntries);

第二快的是锚定正则表达式(丑陋):

Regex lineParse
    = new Regex(@"^\{(<([^>]+)>\s*){3,4}(""([^""]+)""\^\^<([^>]+)>\s*)?\}$",
                RegexOptions.Compiled);
Match m = lineParse.Match(line);
if (m.Groups[2].Captures.Count == 3)
{
    Data data = new Data { C = m.Groups[2].Captures[0].Value,
        S = m.Groups[2].Captures[1].Value, P = m.Groups[2].Captures[2].Value,
        O = m.Groups[4].Value, T = m.Groups[5].Value };
} else {
    Data data = new Data { C = m.Groups[2].Captures[0].Value,
        S = m.Groups[2].Captures[1].Value, P = m.Groups[2].Captures[2].Value,
        O = m.Groups[2].Captures[3].Value, T = String.Empty };
}

1M行随机数据的时序(以String.Split为基线):

Method                #1  Wall (Diff)       #2  Wall (Diff)
------------------------------------------------------------
line.Split                3.6s (1.00x)         3.1s (1.00x)
myRegex.Match             5.1s (1.43x)         3.3s (1.10x)
itDependsRegex.Matches    6.8s (1.88x)         4.4s (1.44x)
stateMachine              8.4s (2.34x)         5.6s (1.82x)
alanM.Matches             9.1s (2.52x)         7.8s (2.52x)
yourRegex.Matches        18.3s (5.06x)        12.1s (3.90x)

更新为包含 @AlanM 和 @itdepends 正则表达式。 Regex.Matches 似乎比 Regex.Match 慢,但是,您为解析器提供的上下文线索越多,它的性能就越好。 @AlanM 使用的单个否定字符类是最容易阅读的,但比最神秘的(我的)慢。向@itdepends 致敬,因为它是产生最快时间的最简单的正则表达式。好的,虽然我认为编写一个状态机来解析该行会很疯狂,但它实际上并没有表现不佳......感谢@RexM 的建议。我还增加了在家中使用 Q6600 (#2) 与工作时使用旧 Xeon (#1) 的次数。

【讨论】:

    【解决方案2】:

    有时状态机比正则表达式快得多。

    【讨论】:

    • +1,我本来想嘲笑你的回答,直到我自己写出来。这不是最差的,也不是最好的。对于所需的工作量来说,令人惊讶的体面......
    • 由于正则表达式只是状态机的一个特例,通过正确的优化,您应该能够击败任何正则表达式引擎。
    【解决方案3】:

    经过一些测试,我想出了:

    @"<(?<capture>[^>]+)>|""(?<capture>[^""]+)"""
    

    该值需要通过 match.Groups[1].Value 访问。

    在我的非科学测试中,它比原始问题中的测试快 75-80%。

    匹配与匹配

    在生产中我通常使用 Match,但在上面使用 Matches。我从来没有真正考虑过性能影响,所以做了一些测试,所以使用完全相同的相同的正则表达式

    for(Match match = regex.Match(input); match.Success; match = match.NextMatch())
    // min 5.01 sec
    // max 5.15 sec
    
    foreach(Match match in regex.Matches(input))
    // min 5.66 sec
    // max 6.07 sec
    

    所以 Match 肯定比 Matches 更快。

    【讨论】:

    • 为你的挖掘增加了时间,从 OP 的正则表达式中减少了大约 3 秒
    • +1,我认为它是正则表达式中提供的回溯和上下文的组合。尖括号为您的正则表达式提供了一个负字符类不会提供的上下文。
    【解决方案4】:

    据我所知,到目前为止提供的正则表达式比它们需要的复杂得多。如果@sixlettervariables 的拆分方法有效,则匹配应该使用这个正则表达式:

    @"[^{}<> ^""]+"
    

    但我仍然希望 String.Split 方法更快。

    【讨论】:

    • 我认为 String.Split 更快的是它不使用正则表达式。用 Regex.Split 试试吧。
    • 哦,我看到你在谈论匹配与匹配性能。但是你只为每一行做一个匹配,你的正则表达式几乎没有回溯的机会。所有其他人都必须每行执行几场比赛。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-01
    • 1970-01-01
    • 2017-06-14
    • 2014-04-27
    • 1970-01-01
    • 2019-01-17
    相关资源
    最近更新 更多