从字符串中删除字符的最快方法

25

我有一个字符串,需要将以下字符删除:'\r'、'\n'和'\t'。

我尝试了三种不同的方法来删除这些字符,并对它们进行了基准测试,以便找到最快的解决方案。

以下是我运行它们1000000次时的方法和执行时间:

如果我只需要删除1或2个字符,则应该是最快的解决方案。但是,当我添加更多字符时,它开始花费更多时间。

str = str.Replace("\r", string.Empty).Replace("\n", string.Empty).Replace("\t", string.Empty);

执行时间 = 1695

对于1个或2个字符,此方法比String.Replace更慢,但对于3个字符,它显示出更好的性能。

string[] split = str.Split(new char[] { '\t', '\r', '\n' }, StringSplitOptions.None);
str = split.Aggregate<string>((str1, str2) => str1 + str2);

执行时间 = 1030

即使只有1个字符,这是最慢的。可能我的正则表达式不是最优的。

str = Regex.Replace(str, "[\r\n\t]", string.Empty, RegexOptions.Compiled);

执行时间 = 3500

以下是我提出的三种解决方案。这里有没有任何更好、更快的解决方案,或者我可以在这段代码中进行哪些改进?

我用于基准测试的字符串:

StringBuilder builder = new StringBuilder();
        builder.AppendFormat("{0}\r\n{1}\t\t\t\r\n{2}\t\r\n{3}\r\n{4}\t\t\r\n{5}\r\n{6}\r\n{7}\r\n{8}\r\n{9}",
         "SELECT ",
         "[Extent1].[CustomerID] AS [CustomerID], ",
         "[Extent1].[NameStyle] AS [NameStyle], ",
         "[Extent1].[Title] AS [Title], ",
           "[Extent1].[FirstName] AS [FirstName], ",
           "[Extent1].[MiddleName] AS [MiddleName], ",
           "[Extent1].[LastName] AS [LastName], ",
           "[Extent1].[Suffix] AS [Suffix], ",
           "[Extent1].[CompanyName] AS [CompanyName], ",
           "[Extent1].[SalesPerson] AS [SalesPerson], ");
        string str = builder.ToString();
7个回答

22
这里是超快速的不安全版本,第二版。
    public static unsafe string StripTabsAndNewlines(string s)
    {
        int len = s.Length;
        char* newChars = stackalloc char[len];
        char* currentChar = newChars;

        for (int i = 0; i < len; ++i)
        {
            char c = s[i];
            switch (c)
            {
                case '\r':
                case '\n':
                case '\t':
                    continue;
                default:
                    *currentChar++ = c;
                    break;
            }
        }
        return new string(newChars, 0, (int)(currentChar - newChars));
    }

以下是基准测试结果(以毫秒为单位剥离1000000个字符串的时间)

    cornerback84的String.Replace方法:        9433
    Andy West的String.Concat方法:            4756
    AviJ的字符数组方法:                      1374
    Matt Howells的字符指针方法:              1163

2
是的,执行时间为195。 - ata
6
顺便说一下,你需要一台新的机器:P - ata
2
这是最近的Xeon - 可能我们的基准测试设置不同。 - Matt Howells
1
我很惊讶这里已经有多久了,没有人提到在处理大字符串时很容易出现堆栈溢出异常。我真的很喜欢不为可能被频繁调用的东西(寻找最快速的方法的人可能会频繁调用它...)分配堆空间的特性,如果你考虑到堆清理,性能差异可能比基准测试显示的要大一些。这需要有条件地使用堆来处理大字符串,以便既快又可靠。 - Rick Velde

10

我相信,通过将新字符串组合成char数组,并在完成后再将其转换为字符串,您将获得最佳性能,如下所示:

string s = "abc";
int len = s.Length;
char[] s2 = new char[len];
int i2 = 0;
for (int i = 0; i < len; i++)
{
    char c = s[i];
    if (c != '\r' && c != '\n' && c != '\t')
        s2[i2++] = c;
}
return new String(s2, 0, i2);

编辑:按照建议,使用String(s2, 0, i2)代替Trim()


一个更正,你需要执行 return new String(s2).TrimEnd('\0'); 以去除尾部的空字符。 同时,执行时间为309。很棒! - ata
2
实际上,我进行了一点修改。您已经保留了新数组的长度即i2。因此,您可以使用return new String(s2,0,i2)而不是修剪。这将使执行时间为255。 - ata

6
String.Join(null, str.Split(new char[] { '\t', '\r', '\n' },
    StringSplitOptions.None));

使用Join()可能会比使用Aggregate()提高性能,因为Join()是专门用于字符串的。

编辑:

实际上,这可能更好:

String.Concat(str.Split(new char[] { '\t', '\r', '\n' },
    StringSplitOptions.None));

不错!我更新了我的答案,使用Concat()代替。值得一试。 - Andy West
使用String.Concat时有轻微的改进。现在,执行时间为734。 - ata

2
更快的速度:
public static string RemoveMultipleWhiteSpaces(string s)
    {
        char[] sResultChars = new char[s.Length];

        bool isWhiteSpace = false;
        int sResultCharsIndex = 0;

        for (int i = 0; i < s.Length; i++)
        {
            if (s[i] == ' ')
            {
                if (!isWhiteSpace)
                {
                    sResultChars[sResultCharsIndex] = s[i];
                    sResultCharsIndex++;
                    isWhiteSpace = true;
                }
            }
            else
            {
                sResultChars[sResultCharsIndex] = s[i];
                sResultCharsIndex++;
                isWhiteSpace = false;
            }
        }

        return new string(sResultChars, 0, sResultCharsIndex);
    }

2

循环遍历字符串并使用一个StringBuilder(使用正确的构造函数参数以避免不必要的内存分配)创建一个新字符串可能更快。


1

试一下这个

string str = "something \tis \nbetter than nothing";
string removeChars = new String(new Char[]{'\n', '\t'});
string newStr = new string(str.ToCharArray().Where(c => !removeChars.Contains(c)).ToArray());

0
string str;
str = str.Replace(Environment.NewLine, string.Empty).Replace("\t", string.Empty);

1
这与已接受答案中的SLOW版本没有区别。 OP正在寻求最快的方法。 - Austin Henley

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接