Подтвердить что ты не робот

Замены регулярных выражений внутри StringBuilder

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

Я столкнулся с проблемой, поскольку функция замены StringBuilder не способна принимать аргументы регулярных выражений.

Я мог бы использовать Regex.Replace в обычной строке, но у меня создается впечатление, что это неэффективно из-за того, что в памяти должны быть созданы две копии строки, поскольку строки .net неизменяемы.

Как только я обновил текст, я планирую записать его в исходный файл.

Какой лучший и самый эффективный способ решить мою проблему?

EDIT

В дополнение к ответам ниже, я нашел следующие вопросы, которые также проливают свет на мою проблему -

4b9b3361

Ответ 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);
}