7

我有一种方法可以替换除我指定的字符之外的每个字符。例如,

ReplaceNot("test. stop; or, not", ".;/\\".ToCharArray(), '*'); 

会回来

“****.*****;***,****”。

现在,这不是过早优化的一个例子。我在网络操作期间多次调用此方法。我发现在较长的字符串上,它会导致一些延迟,删除它会有所帮助。任何有助于加快这一进程的帮助将不胜感激。

    public static string ReplaceNot(this string original, char[] pattern, char replacement)
    {           
        int index = 0;
        int old = -1;

        StringBuilder sb = new StringBuilder(original.Length);

        while ((index = original.IndexOfAny(pattern, index)) > -1)
        {
            sb.Append(new string(replacement, index - old - 1));
            sb.Append(original[index]);
            old = index++;
        }

        if (original.Length - old > 1)
        {
            sb.Append(new string(replacement, original.Length - (old + 1)));
        }

        return sb.ToString();
    }

最后#。我还为 3K 字符串添加了一个测试用例,以 100K 次而不是 1M 次运行,以查看每个缩放的效果。唯一令人惊讶的是正则表达式比其他表达式“缩放得更好”,但这无济于事,因为它开始时非常慢:

用户短*1M长*100K规模
约翰 319 2125 6.66
路加福音 360 2659 7.39
古法 409 2827 6.91
矿山 447 3372 7.54
DirkGently 1094 9134 8.35
迈克尔 1591 12785 8.04
彼得 21106 94386 4.47

更新:我为彼得版本的正则表达式创建一个静态变量,并将其设置为 RegexOptions.Compiled 以公平起见:

用户短*1M长*100K规模
彼得 8997 74715 8.30

Pastebin 链接到我的测试代码,如果有错误请纠正我: http://pastebin.com/f64f260ee

4

6 回答 6

8

你不能像这样使用 Regex.Replace:

Regex regex = new Regex(@"[^.;/\\]");
string s = regex.Replace("test. stop; or, not", "*");
于 2009-05-05T18:55:23.250 回答
6

好吧,在大约 60KB 的字符串上,这将比您的版本快 40%:

public static string ReplaceNot(this string original, char[] pattern, char replacement)
{
    int index = 0;

    StringBuilder sb = new StringBuilder(new string(replacement, original.Length));

    while ((index = original.IndexOfAny(pattern, index)) > -1)
    {
        sb[index] = original[index++];
    }

    return sb.ToString();
}

诀窍是用所有替换字符初始化一个新字符串,因为它们中的大多数都将被替换。

于 2009-05-05T18:55:49.177 回答
4

我不知道这是否会更快,但它避免了更新字符串,以便它们可以附加到字符串生成器,这可能会有所帮助:

    public static string ReplaceNot(this string original, char[] pattern, char replacement)
    {
        StringBuilder sb = new StringBuilder(original.Length);

        foreach (char ch in original) {
            if (Array.IndexOf( pattern, ch) >= 0) {
                sb.Append( ch);
            }
            else {
                sb.Append( replacement);
            }
        }

        return sb.ToString();
    }

如果其中的字符数pattern是任意大小(我猜它通常不会),则可能需要对其进行排序并执行 anArray.BinarySearch()而不是Array.indexOf().

对于如此简单的转换,我敢打赌,它也不会比正则表达式更快。

此外,由于您的字符集pattern可能通常来自字符串(至少这是我对此类 API 的一般经验),为什么您没有方法签名:

public static string ReplaceNot(this string original, string pattern, char replacement)

或者更好的是,在哪里pattern可以是 achar[]string?

于 2009-05-05T19:11:07.363 回答
4

这是给你的另一个版本。我的测试表明它的性能相当不错。

public static string ReplaceNot(
    this string original, char[] pattern, char replacement)
{
    char[] buffer = new char[original.Length];

    for (int i = 0; i < buffer.Length; i++)
    {
        bool replace = true;

        for (int j = 0; j < pattern.Length; j++)
        {
            if (original[i] == pattern[j])
            {
                replace = false;
                break;
            }
        }

        buffer[i] = replace ? replacement : original[i];
    }

    return new string(buffer);
}
于 2009-05-05T23:24:14.663 回答
2

StringBuilder 有一个重载,它接受一个字符和一个计数,因此您不必创建中间字符串来添加到 StringBuilder。通过替换这个,我得到了大约 20% 的改进:

sb.Append(new string(replacement, index - old - 1));

和:

sb.Append(replacement, index - old - 1);

还有这个:

sb.Append(new string(replacement, original.Length - (old + 1)));

和:

sb.Append(replacement, original.Length - (old + 1));

(我测试了你说的代码快了大约四倍,我发现它慢了大约 15 倍......)

于 2009-05-05T19:56:23.230 回答
0

这将是 O(n)。您似乎将所有字母和空格替换为*,为什么不测试当前字符是否为字母/空格并替换它?

于 2009-05-05T18:53:32.693 回答