Самый быстрый способ сопоставить и заменить несколько регулярных выражений?

1

Я знаю, что есть хорошие способы замены фиксированной строки, но что является самым быстрым способом сопоставления и замены многих регулярных выражений, когда у меня есть миллионы строк?

В настоящее время я делаю это:

class Program 
  {
    static IDictionary<string, string> _map = new Dictionary<string, string>() {
            {"(AM)|(PM)", "<time>"},
            // ... 20 more
            {"\\.[0-9]{3}", "<ms>"},
            {"[a-z0-9]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}", "<guid>"},
            {"_\\d+_", "_<number>_"}
    };
    static Regex regex = new Regex(String.Join("|", _map.Keys), RegexOptions.Compiled);

    static void Main(string[] args) {
        // for loop for a million strings
        replace("String here");
    }

    public static replace(string str) {
        return regex.Replace(str, m => "<T>")
    }
  }

Я не мог заменить строку, которую я хотел, используя <T>.

  • 0
    Кроме преобразования вашего словаря строк в статическую строку (которая исключает String.Join ) и повторного использования экземпляра regex , я не думаю, что есть еще что-то, что вы можете сделать.
  • 1
    Вы не должны создавать словарь и регулярные выражения при каждом вызове для replace , что не имеет смысла, если они исправлены. Инициализируйте их как поля вне метода. RegexOptions.Compiled замедляет создание Regex чтобы обеспечить немного лучшую производительность при повторном использовании, но то, как вы используете его сейчас (компиляция при каждом вызове для replace ), значительно замедлит ваш код.
Показать ещё 3 комментария
Теги:
string

1 ответ

1
Лучший ответ

Здесь решение проблемы замены:

private static Func<string, string> CreateReplaceFn()
{
    var map = new List<Tuple<string, string>>
    {
        Tuple.Create("AM|PM", "<time>"),
        // ... 20 more
        Tuple.Create("\\.[0-9]{3}", "<ms>"),
        Tuple.Create("[a-z0-9]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}", "<guid>"),
        Tuple.Create("_\\d+_", "_<number>_")
    };

    var reStr = String.Join("|", map.Select(r => "(" + r.Item1 + ")"));
    var regex = new Regex(reStr, RegexOptions.Compiled);

    return str => regex.Replace(str, match =>
    {
        for (var i = 1; i <= match.Groups.Count; ++i)
        {
            if (match.Groups[i].Success)
                return map[i - 1].Item2;
        }

        return match.Value;
    });
}

Эта функция вернет функцию, которая будет выполнять все замены. Вы должны кэшировать результат этого вызова и повторно использовать его для своих замен. Например:

var replaceFn = CreateReplaceFn();
foreach (var str in myMilionStrings)
    yield return replaceFn(str);

Однако есть одно предостережение: вы не можете использовать неназванные группы захвата внутри ваших шаблонов. Если вам нужно использовать группы, но их не нужно захватывать, замените все (... ) на (?:.. ).

Если вам действительно нужно использовать группы захвата внутри шаблонов, вы должны будете назвать их ((?<name>) а затем \k<name> чтобы ссылаться на него). Это все равно будет работать, так как названные группы всегда будут последними в коллекции Match.Groups.

  • 0
    Я думаю, что он должен использовать ?: Из того, что я помню, группы без захвата намного быстрее и потребляют меньше памяти. Конечно, это не может быть правдой, я сегодня не проверял последние CTP-версии .Net и т. Д. (Может быть, они как-то волшебно оценены?), Но я думаю, что это вполне вероятное предположение, поскольку даже хранение диапазонов от-до занимает когда-то..
  • 0
    @quetzalcoatl да, конечно, но я хочу сказать, что я не знаю, действительно ли ему нужно захватить. Его пример ( (AM)|(PM) и строки статической замены) заставил меня предположить, что ему не нужны группы захвата внутри его шаблонов.
Показать ещё 6 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню