将MatchCollection转换为字符串数组


81

有比这更好的方法来将MatchCollection转换为字符串数组吗?

MatchCollection mc = Regex.Matches(strText, @"\b[A-Za-z-']+\b");
string[] strArray = new string[mc.Count];
for (int i = 0; i < mc.Count;i++ )
{
    strArray[i] = mc[i].Groups[0].Value;
}

PS:mc.CopyTo(strArray,0)引发异常:

无法将源数组中的至少一个元素转换为目标数组类型。

Answers:


164

尝试:

var arr = Regex.Matches(strText, @"\b[A-Za-z-']+\b")
    .Cast<Match>()
    .Select(m => m.Value)
    .ToArray();

1
OfType<Match>()本该使用它而不是Cast<Match>()...然后,结果将是相同的。
Alex

4
@Alex您知道返回的所有内容都是Match,因此无需在运行时再次进行检查。 Cast更有意义。
Servy

2
@DaveBish我在下面发布了一些基准测试代码,OfType<>结果显示速度稍快。
亚历克斯(Alex)

1
@Frontenderman-不,我只是将其与提问者的问题相吻合
Dave Bish 2014年

1
你可能会认为这将是一个简单的命令把一个MatchCollection成一个string[],因为它是Match.ToString()。很明显,在许多Regex用途中需要的最终类型是字符串,因此转换起来应该很容易。
n00dles 2015年

31

Dave Bish的回答很好,并且工作正常。

值得注意的是,虽然更换Cast<Match>()OfType<Match>()意志加快速度。

代码变成:

var arr = Regex.Matches(strText, @"\b[A-Za-z-']+\b")
    .OfType<Match>()
    .Select(m => m.Groups[0].Value)
    .ToArray();

结果是完全相同的(并且以完全相同的方式解决了OP的问题),但是对于大型字符串来说,速度更快。

测试代码:

// put it in a console application
static void Test()
{
    Stopwatch sw = new Stopwatch();
    StringBuilder sb = new StringBuilder();
    string strText = "this will become a very long string after my code has done appending it to the stringbuilder ";

    Enumerable.Range(1, 100000).ToList().ForEach(i => sb.Append(strText));
    strText = sb.ToString();

    sw.Start();
    var arr = Regex.Matches(strText, @"\b[A-Za-z-']+\b")
              .OfType<Match>()
              .Select(m => m.Groups[0].Value)
              .ToArray();
    sw.Stop();

    Console.WriteLine("OfType: " + sw.ElapsedMilliseconds.ToString());
    sw.Reset();

    sw.Start();
    var arr2 = Regex.Matches(strText, @"\b[A-Za-z-']+\b")
              .Cast<Match>()
              .Select(m => m.Groups[0].Value)
              .ToArray();
    sw.Stop();
    Console.WriteLine("Cast: " + sw.ElapsedMilliseconds.ToString());
}

输出如下:

OfType: 6540
Cast: 8743

因此,对于非常长的字符串,Cast()速度较慢。


1
非常令人惊讶!鉴于OfType必须在内部和强制转换之间进行“是”比较(我曾想过?)关于Cast <>为何速度较慢的任何想法?我什么都没有!
Dave Bish 2012年

老实说,我没有任何线索,但是它对我“感觉”正确(OfType <>只是一个过滤器,Cast <>是...很好,是一个演员)
Alex 2012年

更多基准测试表明,此特定结果是由于正则表达式而不是所使用的特定linq扩展所致
Alex

6

我运行了与Alex发布的完全相同的基准,发现有时Cast速度更快,有时OfType速度更快,但两者之间的差异可以忽略不计。但是,尽管难看,for循环始终比其他两个循环都快。

Stopwatch sw = new Stopwatch();
StringBuilder sb = new StringBuilder();
string strText = "this will become a very long string after my code has done appending it to the stringbuilder ";
Enumerable.Range(1, 100000).ToList().ForEach(i => sb.Append(strText));
strText = sb.ToString();

//First two benchmarks

sw.Start();
MatchCollection mc = Regex.Matches(strText, @"\b[A-Za-z-']+\b");
var matches = new string[mc.Count];
for (int i = 0; i < matches.Length; i++)
{
    matches[i] = mc[i].ToString();
}
sw.Stop();

结果:

OfType: 3462
Cast: 3499
For: 2650

linq比for循环慢并不奇怪。对于某些人来说,Linq可能更容易编写,并且以牺牲执行时间为代价来“提高”他们的生产力。有时候可能会很好
gg89 2015年

1
因此,原始帖子确实是最有效的方法。
n00dles 2013年

2

也可以利用这种扩展方法来解决MatchCollection不通用的烦恼。并不是说这有什么大不了,但是几乎可以肯定它比OfTypeor更具性能Cast,因为它只是枚举,这两个都必须执行。

(旁注:我想知道.NET团队是否有可能在将来MatchCollection继承ICollection和的继承通用版本IEnumerable?那么,我们不需要这一额外步骤即可立即提供LINQ转换)。

public static IEnumerable<Match> ToEnumerable(this MatchCollection mc)
{
    if (mc != null) {
        foreach (Match m in mc)
            yield return m;
    }
}

0

考虑以下代码...

var emailAddress = "joe@sad.com; joe@happy.com; joe@elated.com";
List<string> emails = new List<string>();
emails = Regex.Matches(emailAddress, @"([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})")
                .Cast<Match>()
                .Select(m => m.Groups[0].Value)
                .ToList();

1
嗯...这个正则表达式太可怕了。顺便说一句,由于不存在用于验证电子邮件的安全正则表达式,因此请使用MailAddress对象。stackoverflow.com/a/201378/2437521
C. Tewalt 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.