Лучший способ заменить многие строки – обфускация в C #

Я пытаюсь обфускать большой объем данных. Я создал список слов (токенов), которые я хочу заменить, и я заменяю слова один за другим с помощью classа StringBuilder, например:

var sb = new StringBuilder(one_MB_string); foreach(var token in tokens) { sb.Replace(token, "new string"); } 

Это довольно медленно! Есть ли какие-то простые вещи, которые я могу сделать, чтобы ускорить это?

токены – это список из 1000 строк, каждый длиной от 5 до 15 символов.

Вместо того, чтобы выполнять замены в огромной строке (что означает, что вы перемещаете много данных), работайте через строку и заменяйте токен за раз.

Составьте список, содержащий следующий индекс для каждого токена, найдите первый токен, затем скопируйте текст до маркера в результат, а затем замените токен. Затем проверьте, где следующая строка этого токена находится в строке, чтобы сохранить список в актуальном состоянии. Повторяйте, пока не будет найдено больше токенов, затем скопируйте оставшийся текст в результат.

Я сделал простой тест, и этот метод сделал 125000 замен на 1000000 символов строки в 208 миллисекунд.

Классы токенов и токенов:

 public class Token { public string Text { get; private set; } public string Replacement { get; private set; } public int Index { get; set; } public Token(string text, string replacement) { Text = text; Replacement = replacement; } } public class TokenList : List{ public void Add(string text, string replacement) { Add(new Token(text, replacement)); } private Token GetFirstToken() { Token result = null; int index = int.MaxValue; foreach (Token token in this) { if (token.Index != -1 && token.Index < index) { index = token.Index; result = token; } } return result; } public string Replace(string text) { StringBuilder result = new StringBuilder(); foreach (Token token in this) { token.Index = text.IndexOf(token.Text); } int index = 0; Token next; while ((next = GetFirstToken()) != null) { if (index < next.Index) { result.Append(text, index, next.Index - index); index = next.Index; } result.Append(next.Replacement); index += next.Text.Length; next.Index = text.IndexOf(next.Text, index); } if (index < text.Length) { result.Append(text, index, text.Length - index); } return result.ToString(); } } 

Пример использования:

 string text = "This is a text with some words that will be replaced by tokens."; var tokens = new TokenList(); tokens.Add("text", "TXT"); tokens.Add("words", "WRD"); tokens.Add("replaced", "RPL"); string result = tokens.Replace(text); Console.WriteLine(result); 

Вывод:

 This is a TXT with some WRD that will be RPL by tokens. 

Примечание. Этот код не обрабатывает перекрывающиеся жетоны. Если вы, например, имеете токены «ананас» и «яблоко», код не работает должным образом.

Редактировать:
Чтобы заставить код работать с перекрывающимися токенами, замените эту строку:

 next.Index = text.IndexOf(next.Text, index); 

с этим кодом:

 foreach (Token token in this) { if (token.Index != -1 && token.Index < index) { token.Index = text.IndexOf(token.Text, index); } } 

Хорошо, вы понимаете, почему это длится долго, верно?

У вас есть 1 МБ строк, и для каждого токена, замена выполняет итерацию через 1 МБ и создание новой копии 1 МБ. Ну, не точная копия, так как любой найденный токен заменяется новым значением токена. Но для каждого токена вы читаете 1 МБ, добавляете 1 МБ памяти и пишете 1 МБ.

Теперь, можно ли подумать об этом лучше? Как насчет вместо итерации строки 1 МБ для каждого токена, мы вместо этого проводим его один раз.

Перед тем, как идти, мы создадим пустую строку вывода.

Когда мы ходим по исходной строке, если мы найдем токен, мы будем token.length() символы token.length() вперед и выписывать обфусканный токен. В противном случае мы перейдем к следующему символу.

По сути, мы выставляем процесс наизнанку, делая цикл for в длинной строке и в каждом пункте, ищем токен. Чтобы сделать это быстро, нам понадобится быстрый цикл для токенов, поэтому мы помещаем их в какой-то ассоциативный массив (набор).

Я понимаю, почему он длится хорошо, но не уверен в этом. Для каждой строки 1 Мбайт, на которой я выполняю замены, у меня есть от 1 до 2 тысяч токов, которые я хочу заменить. Таким образом, ходячий персонаж по характеру, ищущий любую тысячу токенов, не кажется более быстрым

В общем, что занимает много времени в программировании? Новая память.

Теперь, когда мы создаем StringBuffer, вероятнее всего, что выделяется некоторый объем пространства (скажем, 64 байта, и что всякий раз, когда мы добавляем больше, чем его текущая емкость, он, вероятно, удваивает свое пространство, а затем копирует старый символ буфера к новому. (Возможно, мы сможем перезагрузить C, а не копировать.)

Поэтому, если мы начинаем с 64 байт, чтобы получить до 1 МБ, мы выделяем и копируем: 64, затем 128, затем 256, затем 512, затем 1024, затем 2048 … мы делаем это двадцать раз, чтобы получить до 1 МБ , И, достигнув здесь, мы выделили 1 МБ, чтобы просто выбросить его.

Предварительное выделение, используя что-то, аналогичное функции C ++ ++ reserve() , по крайней мере позволит нам сделать это все сразу. Но это все равно для каждого токена. Вы, по крайней мере, создаете временную строку 1 МБ для каждого токена. Если у вас есть 2000 токенов, вы выделяете около 2 миллиардов байт памяти, и все они заканчиваются 1 MB. Каждая брошюра объемом 1 МБ содержит преобразование предыдущей результирующей строки с применением текущего токена.

И поэтому это так долго.

Теперь да, решая, какой токен применять (если есть), у каждого символа также требуется время. Вы можете использовать регулярное выражение, которое внутренне создает конечный автомат, чтобы использовать все возможности, а не наборный поиск, как я предложил изначально. Но то, что действительно убивает вас, – это время, чтобы выделить всю эту память, для 2000 копий строки 1 МБ.

Дэн Гибсон предлагает:

Сортируйте свои жетоны, чтобы вам не нужно было искать тысячи символов каждого персонажа. Сорт займет некоторое время, но, вероятно, это будет быстрее, поскольку вам не нужно искать тысячи токенов каждого персонажа.

Это было моим аргументом в том, что они помещают их в ассоциативный массив (например, Java HashSet). Но другая проблема заключается в совпадении, например, если один токен «a», а другой – «an» – если есть какие-либо общие префиксы, то есть, как мы сопоставляем?

Вот где ответ Keltex пригодится: он делегирует соответствие Regex, что является отличной идеей, поскольку Regex уже определяет (жадное соответствие) и реализует, как это сделать. После того, как будет выполнено совпадение, мы можем проверить, что захвачено, а затем использовать карту Java (также ассоциативный массив), чтобы найти обфусканный токен для сопоставленного, не запущенного.

Я хотел сконцентрироваться на ответе не только на то, как это исправить, но и на том, почему возникла проблема в первую очередь.

Если вы можете найти свои жетоны через регулярное выражение, вы можете сделать что-то вроде этого:

 RegEx TokenFinder = new Regex("(tokencriteria)"); string newstring = myRegEx.Replace(one_MB_string, new MatchEvaluator(Replacer)); 

Затем определите Replacer как:

 private string Replacer(Match match) { string token= match.Groups[1].Value; return GetObfuscatedString(token); } 

Было бы быстрее строить строку с одним токеном за раз, только если нужно, замените? Для этого GetObfuscatedString() можно реализовать следующим образом:

 string GetObfuscatedString(string token) { if (TokenShouldBeReplaced(token)) return ReplacementForToken(token) else return token; } 

Теперь вы можете добавить каждый маркер в построитель следующим образом:

 StringBuilder sb = new StringBuilder(one_MB_string.Length); foreach (string token in tokens) { sb.Append(da.GetObfuscatedString(token)); } 

Вам нужно сделать только один проход над строкой, и это может быть быстрее.

  • Операция XOR с двумя строками в java
  • Сколько байтов принимает один символ Unicode?
  • Как определить, является ли строка числом?
  • Подстрочный индекс и надстрочный указатель строки в Android
  • Сравнение строки с пустой строкой (Java)
  • Форматировать строки в методе Console.WriteLine
  • Сортировка одной строки в Java
  • Неизменяемость строк в Java
  • Преобразование строки в байтовый массив в C #
  • Замена строк в java, аналогично шаблону скорости
  • Получить индекс n-го вхождения строки?
  • Давайте будем гением компьютера.