有没有更好的方法来替换字符串?

我很惊讶Replace不接受字符数组或字符串数组。我想我可以写我自己的扩展,但我很好奇是否有更好的内置方式来做以下工作?注意最后一个Replace是一个字符串而不是字符。

myString.Replace(';', '\n').Replace(',', '\n').Replace('\r', '\n').Replace('\t', '\n').Replace(' ', '\n').Replace("\n\n", "\n");

当前回答

这是最短的方法:

myString = Regex.Replace(myString, @"[;,\t\r ]|[\n]{2}", "\n");

其他回答

我也摆弄了一下这个问题,发现这里的大多数解决方案都非常缓慢。最快的方法实际上是dodgy_coder发布的LINQ + Aggregate方法。

但我想,这也可能是相当沉重的内存分配取决于有多少旧字符。所以我得出了这个结论:

这里的想法是为当前线程缓存旧字符的替换映射,以实现安全分配。除此之外,只是处理输入的字符数组之后会再次以字符串的形式返回。而字符数组则被尽可能少地修改。

[ThreadStatic]
private static bool[] replaceMap;
public static string Replace(this string input, char[] oldChars, char newChar)
{
    if (input == null) throw new ArgumentNullException(nameof(input));
    if (oldChars == null) throw new ArgumentNullException(nameof(oldChars));
    if (oldChars.Length == 1) return input.Replace(oldChars[0], newChar);
    if (oldChars.Length == 0) return input;

    replaceMap = replaceMap ?? new bool[char.MaxValue + 1];
    foreach (var oldChar in oldChars)
    {
        replaceMap[oldChar] = true;
    }

    try
    {
        var count = input.Length;
        var output = input.ToCharArray();
        for (var i = 0; i < count; i++)
        {
            if (replaceMap[input[i]])
            {
                output[i] = newChar;
            }
        }

        return new string(output);
    }
    finally
    {
        foreach (var oldChar in oldChars)
        {
            replaceMap[oldChar] = false;
        }
    }
}

对我来说,对于实际的输入字符串,这最多是两个分配。由于某些原因,StringBuilder对我来说要慢得多。它比LINQ变体快2倍。

一个. net Core版本,用于将一组已定义的字符串字符替换为特定的字符。它利用了最近引入的Span类型和字符串。创建方法。

其思想是准备一个替换数组,因此不需要对每个字符串字符进行实际的比较操作。因此,替换过程提醒状态机的工作方式。为了避免初始化替换数组的所有项,让我们将oldChar ^ newChar (XOR'ed)值存储在那里,这样做有以下好处:

如果一个字符是不变的:ch ^ ch = 0 -不需要初始化不变的项 最后一个char可以通过XOR'ing: ch ^ repl[ch]: Ch ^ 0 = Ch -不变字符大小写 ch ^ (ch ^ newChar) = newChar -替换的char

因此,唯一的要求是确保替换数组在初始化时为零。我们将使用ArrayPool<char>来避免每次调用ReplaceAll方法时进行分配。并且,为了确保数组为零而不需要调用Array。方法,我们将维护一个专门用于ReplaceAll方法的池。在将替换数组返回到池之前,我们将清除替换数组(仅是精确的项)。

public static class StringExtensions
{
    private static readonly ArrayPool<char> _replacementPool = ArrayPool<char>.Create();

    public static string ReplaceAll(this string str, char newChar, params char[] oldChars)
    {
        // If nothing to do, return the original string.
        if (string.IsNullOrEmpty(str) ||
            oldChars is null ||
            oldChars.Length == 0)
        {
            return str;
        }

        // If only one character needs to be replaced,
        // use the more efficient `string.Replace`.
        if (oldChars.Length == 1)
        {
            return str.Replace(oldChars[0], newChar);
        }

        // Get a replacement array from the pool.
        var replacements = _replacementPool.Rent(char.MaxValue + 1);

        try
        {
            // Intialize the replacement array in the way that
            // all elements represent `oldChar ^ newChar`.
            foreach (var oldCh in oldChars)
            {
                replacements[oldCh] = (char)(newChar ^ oldCh);
            }

            // Create a string with replaced characters.
            return string.Create(str.Length, (str, replacements), (dst, args) =>
            {
                var repl = args.replacements;

                foreach (var ch in args.str)
                {
                    dst[0] = (char)(repl[ch] ^ ch);
                    dst = dst.Slice(1);
                }
            });
        }
        finally
        {
            // Clear the replacement array.
            foreach (var oldCh in oldChars)
            {
                replacements[oldCh] = char.MinValue;
            }

            // Return the replacement array back to the pool.
            _replacementPool.Return(replacements);
        }
    }
}

没有“替换”(仅限Linq):

    string myString = ";,\r\t \n\n=1;;2,,3\r\r4\t\t5  6\n\n\n\n7=";
    char NoRepeat = '\n';
    string ByeBye = ";,\r\t ";
    string myResult = myString.ToCharArray().Where(t => !"STOP-OUTSIDER".Contains(t))
                 .Select(t => "" + ( ByeBye.Contains(t) ? '\n' : t))
                  .Aggregate((all, next) => (
                      next == "" + NoRepeat && all.Substring(all.Length - 1) == "" + NoRepeat
                      ? all : all  + next ) );

就性能而言,这可能不是最好的解决方案,但它确实有效。

var str = "filename:with&bad$separators.txt";
char[] charArray = new char[] { '#', '%', '&', '{', '}', '\\', '<', '>', '*', '?', '/', ' ', '$', '!', '\'', '"', ':', '@' };
foreach (var singleChar in charArray)
{
   str = str.Replace(singleChar, '_');
}

可以使用replace正则表达式。

s/[;,\t\r ]|[\n]{2}/\n/g

S /在开头表示搜索 [和]之间的字符是要搜索的字符(以任何顺序) 第二个/分隔搜索文本和替换文本

用英语来说,这是:

“寻找;或者,或者\t \r或者(空格)或者恰好两个连续的\n,然后把它替换成\n

在c#中,你可以做以下事情:(在导入system . text . regulareexpressions之后)

Regex pattern = new Regex("[;,\t\r ]|[\n]{2}");
pattern.Replace(myString, "\n");