替换C#字符串中的多个字符

zgi*_*rod 162 .net c# string

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

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

myString.Replace(';', '\n').Replace(',', '\n').Replace('\r', '\n').Replace('\t', '\n').Replace(' ', '\n').Replace("\n\n", "\n");
Run Code Online (Sandbox Code Playgroud)

joh*_*tke 193

您可以使用替换正则表达式.

s/[;,\t\r ]|[\n]{2}/\n/g
Run Code Online (Sandbox Code Playgroud)
  • s/ 在开始意味着搜索
  • 之间的字符[]是搜索(以任何顺序)的字符
  • 第二个/分隔搜索文本和替换文本

用英文写的:

"搜索;,\t\r (space) or exactly two sequential \n,并用\n" 替换它"

在C#中,您可以执行以下操作:(导入后System.Text.RegularExpressions)

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

  • 请注意,对于不能由用户配置的简单替换操作,使用正则表达式并不是最佳的,因为它与常规字符串操作相比非常慢,根据我在搜索"c#regex性能替换"时发现的第一篇基准文章,它大约是13时间慢. (11认同)
  • 并且`\ s`实际上相当于`[\ f \n\r\t\t\v]`所以你在那里包含一些原始问题中没有的东西.另外,原始问题要求`替换("\n \n","\n")`你的正则表达式无法处理. (3认同)
  • `\ t`和`\ r``包含在`\ s`中.所以你的正则表达式相当于`[;,\ s]`. (2认同)

Pau*_*lls 105

如果你感觉特别聪明并且不想使用正则表达式:

char[] separators = new char[]{' ',';',',','\r','\t','\n'};

string s = "this;is,\ra\t\n\n\ntest";
string[] temp = s.Split(separators, StringSplitOptions.RemoveEmptyEntries);
s = String.Join("\n", temp);
Run Code Online (Sandbox Code Playgroud)

您可以轻松地将它包装在扩展方法中.

编辑:或者只是等待2分钟,我最终还是会写它:)

public static class ExtensionMethods
{
   public static string Replace(this string s, char[] separators, string newVal)
   {
       string[] temp;

       temp = s.Split(separators, StringSplitOptions.RemoveEmptyEntries);
       return String.Join( newVal, temp );
   }
}
Run Code Online (Sandbox Code Playgroud)

瞧......

char[] separators = new char[]{' ',';',',','\r','\t','\n'};
string s = "this;is,\ra\t\n\n\ntest";

s = s.Replace(separators, "\n");
Run Code Online (Sandbox Code Playgroud)

  • 你是对的.在发布之前我应该​​测量一下.我运行基准测试和`Regex.Replace`比连续多次`string.Replace`调用慢8倍.比'Split` +`Join`慢4倍.请参阅https://gist.github.com/MarcinJuraszek/c1437d925548561ba210a1c6ed144452 (5认同)
  • 内存效率非常低,尤其是对于较大的字符串。 (3认同)
  • 不错的解决方案!只是一个小插件。不幸的是,如果您还希望替换第一个字符,这将不起作用。假设您要替换示例字符串中的“t”字符。Split 方法将删除第一个单词 'this' 的 't',因为它是一个 EmptyEntry。如果您使用 StringSplitOptions.None 而不是 RemoveEmptyEntries,Split 将保留条目,而 Join 方法将添加分隔符。希望这可以帮助 (3认同)
  • @MarcinJuraszek 哈哈...这可能是我第一次听到有人声称内置字符串方法的内存效率低于正则表达式。 (2认同)

dod*_*der 53

您可以使用Linq的Aggregate函数:

string s = "the\nquick\tbrown\rdog,jumped;over the lazy fox.";
char[] chars = new char[] { ' ', ';', ',', '\r', '\t', '\n' };
string snew = chars.Aggregate(s, (c1, c2) => c1.Replace(c2, '\n'));
Run Code Online (Sandbox Code Playgroud)

这是扩展方法:

public static string ReplaceAll(this string seed, char[] chars, char replacementCharacter)
{
    return chars.Aggregate(seed, (str, cItem) => str.Replace(cItem, replacementCharacter));
}
Run Code Online (Sandbox Code Playgroud)


Par*_*Par 18

这是最短的方式:

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

  • 当您在初始值设定项中需要它时,这一个衬里也有帮助。 (2认同)

小智 7

哦,表演恐怖!答案有点过时,但仍然......

public static class StringUtils
{
    #region Private members

    [ThreadStatic]
    private static StringBuilder m_ReplaceSB;

    private static StringBuilder GetReplaceSB(int capacity)
    {
        var result = m_ReplaceSB;

        if (null == result)
        {
            result = new StringBuilder(capacity);
            m_ReplaceSB = result;
        }
        else
        {
            result.Clear();
            result.EnsureCapacity(capacity);
        }

        return result;
    }


    public static string ReplaceAny(this string s, char replaceWith, params char[] chars)
    {
        if (null == chars)
            return s;

        if (null == s)
            return null;

        StringBuilder sb = null;

        for (int i = 0, count = s.Length; i < count; i++)
        {
            var temp = s[i];
            var replace = false;

            for (int j = 0, cc = chars.Length; j < cc; j++)
                if (temp == chars[j])
                {
                    if (null == sb)
                    {
                        sb = GetReplaceSB(count);
                        if (i > 0)
                            sb.Append(s, 0, i);
                    }

                    replace = true;
                    break;
                }

            if (replace)
                sb.Append(replaceWith);
            else
                if (null != sb)
                    sb.Append(temp);
        }

        return null == sb ? s : sb.ToString();
    }
}
Run Code Online (Sandbox Code Playgroud)


tec*_*n23 7

我知道这个问题已经很老了,但我想提供两个更有效的选择:

首先,Paul Walls 发布的扩展方法很好,但可以通过使用 StringBuilder 类来提高效率,该类类似于字符串数据类型,但专门针对您将多次更改字符串值的情况。这是我使用 StringBuilder 制作的扩展方法的版本:

public static string ReplaceChars(this string s, char[] separators, char newVal)
{
    StringBuilder sb = new StringBuilder(s);
    foreach (var c in separators) { sb.Replace(c, newVal); }
    return sb.ToString();
}
Run Code Online (Sandbox Code Playgroud)

我运行此操作 100,000 次,使用 StringBuilder 花费了 73 毫秒,而使用字符串则花费了 81 毫秒。因此,差异通常可以忽略不计,除非您运行许多操作或使用巨大的字符串。

其次,这是一个您可以使用的 1 衬垫循环:

foreach (char c in separators) { s = s.Replace(c, '\n'); }
Run Code Online (Sandbox Code Playgroud)

我个人认为这是最好的选择。它效率很高,并且不需要编写扩展方法。在我的测试中,它只用了 63 毫秒就运行了 10 万次迭代,使其效率最高。这是上下文中的一个示例:

string s = "this;is,\ra\t\n\n\ntest";
char[] separators = new char[] { ' ', ';', ',', '\r', '\t', '\n' };
foreach (char c in separators) { s = s.Replace(c, '\n'); }
Run Code Online (Sandbox Code Playgroud)

此示例中的前两行归功于 Paul Walls。


sɐu*_*qɐp 6

您也可以简单地编写这些字符串扩展方法,并将它们放在解决方案中的某个位置:

using System.Text;

public static class StringExtensions
{
    public static string ReplaceAll(this string original, string toBeReplaced, string newValue)
    {
        if (string.IsNullOrEmpty(original) || string.IsNullOrEmpty(toBeReplaced)) return original;
        if (newValue == null) newValue = string.Empty;
        StringBuilder sb = new StringBuilder();
        foreach (char ch in original)
        {
            if (toBeReplaced.IndexOf(ch) < 0) sb.Append(ch);
            else sb.Append(newValue);
        }
        return sb.ToString();
    }

    public static string ReplaceAll(this string original, string[] toBeReplaced, string newValue)
    {
        if (string.IsNullOrEmpty(original) || toBeReplaced == null || toBeReplaced.Length <= 0) return original;
        if (newValue == null) newValue = string.Empty;
        foreach (string str in toBeReplaced)
            if (!string.IsNullOrEmpty(str))
                original = original.Replace(str, newValue);
        return original;
    }
}
Run Code Online (Sandbox Code Playgroud)


像这样称呼他们:

"ABCDE".ReplaceAll("ACE", "xy");
Run Code Online (Sandbox Code Playgroud)

xyBxyDxy


和这个:

"ABCDEF".ReplaceAll(new string[] { "AB", "DE", "EF" }, "xy");
Run Code Online (Sandbox Code Playgroud)

xyCxyF


Fab*_*Fab 5

字符串只是不可变的char数组

您只需要使其可变即可:

  • 通过使用 StringBuilder
  • 进入unsafe世界并与指针一起玩(虽然很危险)

并尝试以最少的次数遍历字符数组。请注意HashSet此处,因为它避免遍历循环内的字符序列。如果您需要更快的查找,则可以HashSet通过优化的查找来代替char(基于array[256])。

StringBuilder的示例

public static void MultiReplace(this StringBuilder builder, 
    char[] toReplace, 
    char replacement)
{
    HashSet<char> set = new HashSet<char>(toReplace);
    for (int i = 0; i < builder.Length; ++i)
    {
        var currentCharacter = builder[i];
        if (set.Contains(currentCharacter))
        {
            builder[i] = replacement;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑-优化版本

public static void MultiReplace(this StringBuilder builder, 
    char[] toReplace,
    char replacement)
{
    var set = new bool[256];
    foreach (var charToReplace in toReplace)
    {
        set[charToReplace] = true;
    }
    for (int i = 0; i < builder.Length; ++i)
    {
        var currentCharacter = builder[i];
        if (set[currentCharacter])
        {
            builder[i] = replacement;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以像这样使用它:

var builder = new StringBuilder("my bad,url&slugs");
builder.MultiReplace(new []{' ', '&', ','}, '-');
var result = builder.ToString();
Run Code Online (Sandbox Code Playgroud)

  • 请记住,字符串是 .net 中的“wchar_t”,您仅替换所有可能字符的子集(并且您需要 65536 个布尔值来优化它......) (2认同)

And*_*yCh 5

用于将一组定义的字符串字符替换为特定字符的 .NET Core 版本。它利用了最近引入的Span类型和string.Create方法。

这个想法是准备一个替换数组,因此每个字符串字符不需要实际的比较操作。因此,替换过程提醒了状态机的工作方式。为了避免初始化替换数组的所有项目,让我们oldChar ^ newChar在那里存储(异或)值,这会带来以下好处:

  • 如果字符未更改:ch ^ ch = 0- 无需初始化未更改的项目
  • 最终的字符可以通过异或找到ch ^ repl[ch]::
    • ch ^ 0 = ch- 没有改变字符大小写
    • ch ^ (ch ^ newChar) = newChar- 替换字符

因此,唯一的要求是确保替换数组在初始化时为零。我们将使用它ArrayPool<char>来避免每次ReplaceAll调用该方法时进行分配。而且,为了确保数组清零而不需要昂贵的Array.Clear方法调用,我们将维护一个专用于该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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)