更有效的正则表达式或替代方案?

时间:2009-02-04 23:23:44

标签: c# .net regex

我的文件有一百多万行。

 {<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

评论中的其他信息:“我看到的糟糕表现实际上是由于我在代码中设置的条件断点。没有这个断点,一切都很快。如果有人有任何改进的想法我会对“-Eric Sc​​hoonover

感兴趣

4 个答案:

答案 0 :(得分:19)

最快(如下所示)是一个简单的字符串拆分:

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.85x)         4.4s (1.44x)
stateMachine              8.4s (2.34x)         5.6s (1.82x)
alanM.Matches             9.1s (2.52x)         7.8s (2.56x)
yourRegex.Matches        18.3s (5.06x)        12.1s (1.82x)

更新以包含@AlanM和@itdepends正则表达式。看起来Regex.Matches比Regex.Match慢,但是,您为解析器提供的上下文线索越多,它的执行效果就越好。 @AlanM使用的单个负字符类是最简单的读取,但比最神秘的(我的)慢。向@itdepends致敬,以获得产生最快时间的最简单的正则表达式。好吧,虽然我认为编写一个状态机来解析该行会很疯狂,但它实际上并没有表现得很差......对于@RexM的建议感到荣幸。我还从家里的Q6600(#2)和工作中的旧Xeon(#1)增加了时间。

答案 1 :(得分:6)

有时状态机明显快于正则表达式。

答案 2 :(得分:2)

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

@"<(?<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

所以匹配肯定比匹配更快。

答案 3 :(得分:1)

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

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

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