Преобразование MatchCollection в строковый массив

Есть ли лучший способ конвертировать 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) выдает исключение:

По крайней мере, один элемент в исходном массиве не может быть приведен к типу целевого массива.

Ответ 1

Try:

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

Ответ 2

Ответ Дейва Биша хорош и работает правильно.

Стоит отметить, что замена Cast<Match>() на OfType<Match>() ускорит процесс.

Код wold станет:

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() поэтому медленнее.

Ответ 3

Я запустил тот же тест, что и Алекс, и обнаружил, что иногда 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

Ответ 4

Можно также использовать этот метод расширения, чтобы справиться с раздражением MatchCollection не являющимся универсальным. Не то, чтобы это было большим делом, но это почти наверняка более производительно, чем OfType или 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;
    }
}

Ответ 5

Рассмотрим следующий код...

var emailAddress = "[email protected]; [email protected]; [email protected]";
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();

Удачи!