Regex замены внутри StringBuilder - PullRequest
       7

Regex замены внутри StringBuilder

20 голосов
/ 17 августа 2010

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

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

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

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

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

РЕДАКТИРОВАТЬ

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

Ответы [ 4 ]

27 голосов
/ 17 августа 2010

Лучшее и наиболее эффективное решение для вашего времени - это сначала попробовать самый простой подход: забудьте StringBuilder и просто используйте Regex.Replace. Затем выясните, насколько он медленный - вполне может быть достаточно хорошим. Не забудьте попробовать регулярное выражение как в скомпилированном, так и в некомпилированном режиме.

Если это не достаточно достаточно быстро, рассмотрите возможность использования StringBuilder для любых замен, которые вы можете выразить просто, а затем используйте Regex.Replace для остальных. Вы также можете попробовать объединить замены, сократив количество используемых регулярных выражений (и, следовательно, промежуточных строк).

4 голосов
/ 30 июля 2014

У вас есть 3 варианта:

  1. Делайте это неэффективным способом со строками, как другие рекомендовали здесь.

  2. Используйте .Matches() вызовите ваш Regex объект и эмулируйте работу .Replace() (см. # 3).

  3. Адаптируйте реализацию Mono Regex для создания Regex, которыйпринимает StringBuilder (и, пожалуйста, поделитесь им здесь!) Почти вся работа уже сделана для вас в Mono, но потребуется время, чтобы выяснить, какие части заставляют ее работать в их собственной библиотеке.Regex Mono использует реализацию JVM Novell 2002 года, как ни странно, Regex.

В Mono:

System.Text.RegularExpressions.Regex использует RxCompiler для создания IMachineFactory в форме RxInterpreterFactory, что неудивительно, что IMachine с составляет RxInterpreter с.Заставить их излучать - это большая часть того, что вам нужно сделать, хотя, если вы просто хотите узнать, как все это структурировано для повышения эффективности, значительная часть того, что вы ищете, находится в базовом классе, 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 - единственный, который действительно делает это правильно.

1 голос
/ 20 июля 2012

Я не уверен, помогает ли это вашему сценарию или нет, но я столкнулся с некоторыми предельными значениями потребления памяти с помощью Regex, и мне был нужен простой метод расширения с подстановочными символами на StringBuilder, чтобы пройти мимо него.Если вам нужно сложное сопоставление с Regex и / или обратные ссылки, это не подойдет, но если просто * или?замены с подстановочными знаками (с буквальным текстом «заменить») выполнят работу за вас, тогда обходной путь в конце моего вопроса должен по крайней мере дать вам повышение:

Кто-нибудь реализовал Regexи / или Xml-анализатор вокруг StringBuilders или Streams?

0 голосов
/ 14 мая 2014

Вот метод расширения, который вы можете использовать для достижения желаемого. Он принимает 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);
}
...