C# 替换多个字符串的更好方法-C中的模糊处理#

C# 替换多个字符串的更好方法-C中的模糊处理#,c#,optimization,string,performance,obfuscation,C#,Optimization,String,Performance,Obfuscation,我试图混淆大量的数据。我已经创建了一个要替换的单词(标记)列表,我正在使用StringBuilder类逐个替换这些单词,如下所示: var sb = new StringBuilder(one_MB_string); foreach(var token in tokens) { sb.Replace(token, "new string"); } 相当慢!我能做些简单的事情来加速它吗 tokens是一个包含大约1000个字符串的列表,每个字符串的长度为5到15个字符。一次生成一个

我试图混淆大量的数据。我已经创建了一个要替换的单词(标记)列表,我正在使用StringBuilder类逐个替换这些单词,如下所示:

 var sb = new StringBuilder(one_MB_string);
 foreach(var token in tokens)
 {
   sb.Replace(token, "new string");
 }
相当慢!我能做些简单的事情来加速它吗


tokens是一个包含大约1000个字符串的列表,每个字符串的长度为5到15个字符。

一次生成一个标记,仅在需要时替换,是否会更快?为此,
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));
}
RegEx TokenFinder = new Regex("(tokencriteria)");
string newstring = myRegEx.Replace(one_MB_string, new MatchEvaluator(Replacer));

您只需在字符串上传递一次,速度可能会更快。

如果您可以通过正则表达式找到标记,您可以执行以下操作:

StringBuilder sb = new StringBuilder(one_MB_string.Length);
foreach (string token in tokens)
{
    sb.Append(da.GetObfuscatedString(token));
}
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);
}

好吧,你知道为什么要花很长时间,对吧

您有1MB字符串,对于每个令牌,replace将迭代1MB并创建一个新的1MB副本。好吧,不是一个精确的副本,因为找到的任何令牌都会被新的令牌值替换。但对于每个令牌,您都在读取1MB,更新1MB存储,并写入1MB

现在,我们能想出更好的方法吗?与其为每个令牌迭代1MB字符串,不如遍历它一次

在遍历它之前,我们将创建一个空的输出字符串

当我们遍历源字符串时,如果我们找到一个标记,我们将向前跳转
token.length()
字符,并写出模糊标记。否则我们将进入下一个角色

从本质上说,我们将进程从内到外,在长字符串上执行for循环,并在每个点上寻找一个标记。为了加快速度,我们需要令牌的快速循环,所以我们将它们放入某种关联数组(集合)


我明白为什么要花很长时间好吧, 但不确定是否有问题。每1MB 我正在执行的字符串 我有一千到两千个替补 我想替换托坎。所以走路 逐个字符查找任何 一千个代币中的一个似乎没有 更快

一般来说,编程需要的时间最长是什么?新的记忆

现在,当我们创建一个StringBuffer时,可能发生的情况是分配了一些空间(比如说,64字节,并且每当我们附加的空间超过其当前容量时,它可能会将其空间增加一倍。然后将旧的字符缓冲区复制到新的字符缓冲区。(我们可以使用C的realloc,而不必复制。)

因此,如果我们从64个字节开始,为了达到1MB,我们分配并复制: 64、128、256、512、1024、2048……我们这样做了20次,以获得1MB。在这里,我们分配1MB只是为了扔掉它

通过使用类似于C++的
reserve()的东西进行预分配
函数,至少让我们一次完成所有操作。但每个令牌仍然是一次完成的。您至少为每个令牌生成一个1MB临时字符串。如果您有2000个令牌,您将分配大约20亿字节的内存,所有这些都将以1MB结束。每个1MB一次性包含前一个生成字符串的转换,并应用当前令牌

这就是为什么要花这么长时间

现在是,决定应用哪个令牌(如果有),在每个字符上,也需要时间。您可能希望使用正则表达式,它在内部构建一个状态机来运行所有可能的操作,而不是像我最初建议的那样使用集合查找。但真正让您头疼的是分配所有内存的时间,一个1MB字符串的2000个副本

丹·吉布森建议:


对您的代币进行排序,这样您就不必 每人找一千个代币 性格。这类人需要一些时间 时间,但它可能会结束 因为你不必这么做,所以速度更快 每个搜索数千个令牌 性格

这就是我将它们放入关联数组(例如Java HashSet)的原因。但另一个问题是匹配,例如,如果一个标记是“a”,另一个是“a”——如果有任何公共前缀,即如何匹配

这就是Keltex的答案派上用场的地方:他将匹配委托给正则表达式,这是一个好主意,因为正则表达式已经定义了(贪婪匹配)并实现了如何做到这一点。匹配完成后,我们可以检查捕获的内容,然后使用Java映射(也是一个关联数组)为匹配的未模糊标记查找模糊标记


我想集中回答的问题不仅仅是如何解决这个问题,还包括为什么首先会出现问题。

与其在一个巨大的字符串中进行替换(这意味着要移动大量数据),不如遍历该字符串,一次替换一个令牌

为每个标记创建一个包含下一个索引的列表,找到第一个标记,然后将文本复制到标记的结果中,然后替换标记。然后检查该标记下一次出现的位置在字符串中,以使列表保持最新。重复此操作,直到找不到更多标记,然后复制剩余的文本结果如何

我做了一个简单的测试,这个方法在208毫秒内对1000000个字符串进行了125000次替换

令牌和令牌列表类:

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<Token>{

    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();
    }

}
输出:

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);
    }
}
foreach(此处为令牌){
if(token.Index!=-1&&token.Index
缓慢发生在哪里?是在da.GetFuscatedString(令牌)中,还是与您拥有的令牌数有关?在替换中,而不是da.GetFuscatedString(令牌)。90%的时间用于替换,10%的时间用于替换