Замены регулярных выражений внутри StringBuilder
Я пишу содержимое текстового файла в StringBuilder, и затем я хочу выполнить несколько операций поиска/замены текста, содержащегося в StringBuilder, с помощью регулярных выражений.
Я столкнулся с проблемой, поскольку функция замены StringBuilder не способна принимать аргументы регулярных выражений.
Я мог бы использовать Regex.Replace в обычной строке, но у меня создается впечатление, что это неэффективно из-за того, что в памяти должны быть созданы две копии строки, поскольку строки .net неизменяемы.
Как только я обновил текст, я планирую записать его в исходный файл.
Какой лучший и самый эффективный способ решить мою проблему?
EDIT
В дополнение к ответам ниже, я нашел следующие вопросы, которые также проливают свет на мою проблему -
Ответы
Ответ 1
Лучшее и эффективное решение для вашего времени - сначала попробуйте простейший подход: забудьте StringBuilder
и просто используйте Regex.Replace
. Затем выясните, насколько это медленное - это может быть очень хорошо. Не забудьте попробовать регулярное выражение как в скомпилированном, так и в некомпилированном режиме.
Если это не достаточно быстро, рассмотрите возможность использования StringBuilder
для любых замен, которые вы можете выразить просто, а затем используйте Regex.Replace
для остальных. Вы также можете захотеть попытаться объединить замены, уменьшив количество регулярных выражений (и, следовательно, промежуточных строк).
Ответ 2
У вас есть 3 варианта:
-
Сделайте это неэффективно со строками, которые другие рекомендовали здесь.
-
Используйте вызов .Matches()
для вашего объекта Regex
и эмулируйте способ .Replace()
(см. № 3).
-
Адаптируйте реализацию Mono Regex
, чтобы построить Regex
, который принимает StringBuilder
(и, пожалуйста, поделитесь им здесь!) Почти вся работа уже выполнена для вас в Mono, но это займет время, чтобы выслушать части, которые заставляют его работать в своей собственной библиотеке. Mono Regex
использует реализацию JVM Novell 2002 Regex
, как ни странно.
В моно:
System.Text.RegularExpressions.Regex
использует RxCompiler
для создать экземпляр IMachineFactory в виде RxInterpreterFactory
, что неудивительно делает IMachine
как RxInterpreter
s. Получение тех, кто испускает, - это большая часть того, что вам нужно сделать, хотя, если вы просто хотите узнать, как все это структурировано для повышения эффективности, то значительная часть того, что вы ищете, находится в базовом классе, BaseMachine
.
В частности, в BaseMachine
находится материал на основе StringBuilder
. В методе LTRReplace
он сначала создает экземпляр StringBuilder с исходной строкой, и все, что происходит оттуда, чисто основано на StringBuilder. На самом деле очень раздражает то, что в Regex нет методов StringBuilder, если предположить, что внутренняя реализация Microsoft.Net аналогична.
Возвращаясь к предложению 2, вы можете имитировать поведение LTRReplace
, вызвав .Matches()
, отслеживая, где вы находитесь в исходной строке, и выполните цикл:
var matches = regex.Matches(original);
var sb = new StringBuilder(original.Length);
int pos = 0; // position in original string
foreach(var match in matches)
{
sb.Append(original.Substring(pos, match.Index)); // Append the portion of the original we skipped
pos = match.Index;
// Make any operations you like on the match result, like your own custom Replace, or even run another Regex
pos += match.Value.Length;
}
sb.Append(original.Substring(pos, original.Length - 1));
Но это только сэкономит вам несколько строк - метод mod-Mono - это единственный, который действительно делает это правильно.
Ответ 3
Я не уверен, помогает ли это вашему сценарию или нет, но я столкнулся с некоторыми потоками потребления памяти с помощью Regex, и мне понадобился простой метод расширения подстановочных знаков на StringBuilder, чтобы пропустить его. Если вам нужно сложное соответствие Regex и/или обратные ссылки, это не будет сделано, но если вы просто * или? заменители подстановочных знаков (с буквальным текстом "заменить" ) выполнили бы эту работу для вас, тогда обходной путь в конце моего вопроса здесь должен по крайней мере дать вам импульс:
Кто-нибудь реализовал парсер Regex и/или Xml вокруг StringBuilders или потоков?
Ответ 4
Здесь вы можете использовать метод расширения, который вы можете использовать для достижения желаемого. Требуется Dictionary
, где ключ - это шаблон, который вы ищете, и значение - это то, что вы хотите заменить. Вы по-прежнему создаете копии входящей строки, но вам нужно иметь дело только с этим, вместо того, чтобы создавать копии для нескольких вызовов на Regex.Replace
.
public static StringBuilder BulkReplace(this StringBuilder source, IDictionary<string, string> replacementMap)
{
if (source.Length == 0 || replacementMap.Count == 0)
{
return source;
}
string replaced = Regex.Replace(source.ToString(), String.Join("|", replacementMap.Keys.Select(Regex.Escape).ToArray()), m => replacementMap[m.Value], RegexOptions.IgnoreCase);
return source.Clear().Append(replaced);
}