Я знаю, что есть хорошие способы замены фиксированной строки, но что является самым быстрым способом сопоставления и замены многих регулярных выражений, когда у меня есть миллионы строк?
В настоящее время я делаю это:
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>
.
Здесь решение проблемы замены:
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
.
?:
Из того, что я помню, группы без захвата намного быстрее и потребляют меньше памяти. Конечно, это не может быть правдой, я сегодня не проверял последние CTP-версии .Net и т. Д. (Может быть, они как-то волшебно оценены?), Но я думаю, что это вполне вероятное предположение, поскольку даже хранение диапазонов от-до занимает когда-то..
(AM)|(PM)
и строки статической замены) заставил меня предположить, что ему не нужны группы захвата внутри его шаблонов.
String.Join
) и повторного использования экземпляраregex
, я не думаю, что есть еще что-то, что вы можете сделать.replace
, что не имеет смысла, если они исправлены. Инициализируйте их как поля вне метода.RegexOptions.Compiled
замедляет созданиеRegex
чтобы обеспечить немного лучшую производительность при повторном использовании, но то, как вы используете его сейчас (компиляция при каждом вызове дляreplace
), значительно замедлит ваш код.