C# 是否有可能从字符串列表中快速提取片段中标记内部的特定值?

C# 是否有可能从字符串列表中快速提取片段中标记内部的特定值?,c#,regex,list,optimization,C#,Regex,List,Optimization,我使用两步正则表达式来提取字符串列表中特定标记首次出现的值: Regex regexComplete = new Regex( @"MyNumberMarker" + @"[\d]+" + @"[\s]+Endmarker" ); Regex regexOnlyNumber = new Regex( @"MyNumberMarker" + @"[\d]+" ); int indexmyNumber = eintraegeListe.FindIndex(

我使用两步正则表达式来提取字符串列表中特定标记首次出现的值:

Regex regexComplete = new Regex(
    @"MyNumberMarker"
    + @"[\d]+"
    + @"[\s]+Endmarker"
);

Regex regexOnlyNumber = new Regex(
    @"MyNumberMarker"
    + @"[\d]+"
);

int indexmyNumber = eintraegeListe.FindIndex(
    5,
    10000,
    x => regexComplete.IsMatch(x)
);

if (indexmyNumber >= 0)
{
    int myNumber = 0;
    string myNumberString = regexOnlyNumber.Match(regexComplete.Match(eintraegeListe[indexmyNumber]).Value).Value;
    myNumberString = myNumberString.Replace("MyNumberMarker", "").Replace("\n", "").Replace("\r", "").Trim();

    if (Int32.TryParse(myNumberString, out myNumber))
    {
        return myNumber;
    }
}
可以看出,我真正想要的值位于“MyNumberMarker”和“Endmarker”之间。它位于我使用findIndex命令搜索的列表的特定部分。然后我使用正则表达式提取完整的value+标记,并将其减少为“仅”开始标记和值,然后手动删除开始标记,所有标记都可以是空白(包括\n和\r)

现在,这工作相当好,因为预期,但如果我这样做了几千次,它是相当缓慢的最后。因此,我的问题是。 有没有更好(更快)的方法

请注意:eIntrageListe可以有100到30000个条目

例如,如果我有以下小列表:

[0]This is a test
[1]22.09.2015 01:00:00
[2]Until 22.09.2015 03:00:00
[3]................................
[4]................................
[5]........ TESTDATA
[6]...............................
[7]................................
[8]MyNumberMarker519 Endmarker
[9]This is a small
[10]Slice of Test data with
[11]520 - 1 as data.

我希望返回519

因为您返回的是单个项目,所以代码通过
FindIndex的性能与此无关:它只执行一次,并且只需要一个字符串,因此在任何现代硬件上都应该在微秒内完成

占用大量CPU的代码位于
x=>regexComplete.IsMatch(x)
call中。您可以看出此代码大部分时间都在返回
false
,因为循环是第一次返回
true

这意味着您应该针对负面情况进行优化,即尽快返回
false
。实现这一点的一种方法是在使用正则表达式之前查找
“MyNumberMarker”
。如果没有标记,立即返回
false
;否则,请继续使用正则表达式,并从找到标记的位置开始:

int indexmyNumber = eintraegeListe.FindIndex(
    5,
    10000,
    x => {
        // Scan the string for the marker in non-regex mode
        int pos = x.IndexOf("MyNumberMarker", StringComparison.Ordinal);
        // If the marker is not there, do not bother with regex, and return false
        return pos < 0
             ? false
             // Only if the marker is there, check the match with regex.
             : regexComplete.IsMatch(x, pos);
    }
);
int indexmyNumber=eintrageliste.FindIndex(
5.
10000,
x=>{
//在非正则模式下扫描字符串以查找标记
int pos=x.IndexOf(“MyNumberMarker”,StringComparison.Ordinal);
//如果标记不在那里,则不要使用正则表达式,并返回false
返回位置<0
?错误
//仅当标记存在时,才检查与正则表达式的匹配。
:regexComplete.IsMatch(x,pos);
}
);

实际上,您可以将两个regexp合并为一个包含捕获组的1,该捕获组允许您直接通过组名访问数字序列(此处,
“number”

然后:

if (indexmyNumber >= 0)
{
    int myNumber = 0;
    string myNumberString = regexComplete.Match(eintraegeListe[indexmyNumber]).Groups["number"].Value;
    if (Int32.TryParse(myNumberString, out myNumber))
    {
        return myNumber;
    }
}

一个原始输入和所需输出的示例会有所帮助。添加了一个只有11个条目的非常基本的示例。您是否尝试过在创建正则表达式时使用选项
RegexOptions.Singleline | RegexOptions.Compiled
?查看在哪里初始化正则表达式对象?如果在循环内,则速度会很慢。此外,只需使用一个正则表达式:
regex regexComplete=new regex(@“MyNumberMarker(?\d+)\s+Endmarker”),然后您可以通过
rx.Match(input).Groups[“number”].Value
访问它。另外,请查看。我希望现在它更清晰。如果你在这个问题上需要更多的帮助,请告诉我。Yepp有效。更改前后的差异为:1到10(就所需时间而言)。所以它的工作速度几乎和我想要的一样快。
int indexmyNumber = eintraegeListe.FindIndex(5, 10000, x => x.Contains("MyNumberMarker") && regexComplete.IsMatch(x));
if (indexmyNumber >= 0)
{
    int myNumber = 0;
    string myNumberString = regexComplete.Match(eintraegeListe[indexmyNumber]).Groups["number"].Value;
    if (Int32.TryParse(myNumberString, out myNumber))
    {
        return myNumber;
    }
}