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

本文关键字:字符 字符串 替换 | 更新日期: 2023-09-27 18:07:24

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

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

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

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

可以使用替换正则表达式

s/[;,'t'r ]|['n]{2}/'n/g
  • 开头的s/表示搜索
  • []之间的字符是要搜索的字符(任意顺序)
  • 第二个/分隔搜索文本和替换文本

在英语中,这是:

"搜索;,'t'r(空格)或恰好两个连续的'n并将其替换为System.Text.RegularExpressions "

在c#中,您可以做以下操作:(在导入StringBuilder之后)

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

如果你觉得自己特别聪明,不想使用Regex:

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

你也可以毫不费力地把它封装在一个扩展方法中。

编辑:或者等两分钟,我最后还是会写的:)

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

瞧……

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

您可以使用Linq的聚合函数:

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'));

下面是扩展方法:

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

扩展方法使用示例:

string snew = s.ReplaceAll(chars, ''n');

这是最短的路径:

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

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

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

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

你只需要让它可变:

  • 可以使用unsafe
  • 进入HashSet世界,玩指针(虽然危险)

并尝试遍历字符数组的次数最少。注意这里的HashSet,因为它避免在循环内遍历字符序列。如果您需要更快的查找,您可以用array[256]的优化查找(基于Span)替换char

使用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;
        }
    }
}

编辑-优化版本(仅对ASCII有效)

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

然后像这样使用:

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

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

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


这样调用它们:

"ABCDE".ReplaceAll("ACE", "xy");

xyBxyDxy


这:

"ABCDEF".ReplaceAll(new string[] { "AB", "DE", "EF" }, "xy");

xyCxyF

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

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

  • 如果char没有改变:ch ^ repl[ch] -不需要初始化不变的项
  • 最后一个字符可以通过XOR命令找到:ch ^ 0 = ch:
    • ch ^ (ch ^ newChar) = newChar -不改变字符大小写
    • ArrayPool<char> -取代char
因此,唯一的要求是确保替换数组在初始化时为零。我们将使用ReplaceAll来避免每次调用Array.Clear方法时的分配。并且,为了确保数组为零而不需要对ReplaceAll方法进行昂贵的调用,我们将维护一个专用于StringBuilder方法的池。在将替换数组返回到池之前,我们将清除替换数组(只清除精确的项)。
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);
        }
    }
}

使用RegEx。替换,像这样:

  string input = "This is   text with   far  too   much   " + 
                 "whitespace.";
  string pattern = "[;,]";
  string replacement = "'n";
  Regex rgx = new Regex(pattern);
  string result = rgx.Replace(input, replacement);

这里有关于RegEx MSDN文档的更多信息。

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

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

我运行了100,000次这个操作,使用StringBuilder花费了73ms,而使用string花费了81ms。所以差别通常可以忽略不计,除非你运行很多操作或者使用一个大字符串。

其次,这里有一个1行循环,你可以使用:
foreach (char c in separators) { s = s.Replace(c, ''n'); }
我个人认为这是最好的选择。它非常高效,而且不需要编写扩展方法。在我的测试中,它在63ms内运行了100k次迭代,这是最有效的。下面是上下文中的一个例子:
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'); }

感谢Paul Walls在这个例子中的前两行

性能方面,这可能不是最好的解决方案,但它是有效的。

var str = "filename:with&bad$separators.txt";
char[] charArray = new char[] { '#', '%', '&', '{', '}', '''', '<', '>', '*', '?', '/', ' ', '$', '!', '''', '"', ':', '@' };
foreach (var singleChar in charArray)
{
   str = str.Replace(singleChar, '_');
}
string ToBeReplaceCharacters = @"~()@#$%&amp;+,'&quot;&lt;&gt;|;'/*?";
string fileName = "filename;with<bad:separators?";
foreach (var RepChar in ToBeReplaceCharacters)
{
    fileName = fileName.Replace(RepChar.ToString(), "");
}

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

但是我想,这可能也会占用很大的内存,这取决于有多少旧字符。所以我写了这个:

这里的想法是为当前线程缓存旧字符的替换映射,以安全分配。除此之外,只是处理输入的字符数组稍后又作为字符串返回。而字符数组的修改则尽可能少。
[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;
        }
    }
}

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

没有"Replace"(仅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 ) );

构建了我自己的解决方案,并查看了这里使用的解决方案,我利用了一个不使用复杂代码并且通常对大多数参数有效的答案。

  1. 覆盖其他方法更合适的基本情况。如果没有要替换的字符,则返回原始字符串。如果只有一个,则使用Replace方法。
  2. 使用StringBuilder并将容量初始化为原始字符串的长度。毕竟,如果只是替换字符,正在构建的新字符串将具有与原始字符串相同的长度。这确保只为新字符串分配1个内存。
  3. 假设'char'长度可以小或大都会影响性能。大型集合更适合使用哈希集,而较小的集合则不适合。这是混合字典的一个近乎完美的用例。一旦集合变得太大,它们就会切换到使用基于Hash的查找。但是,我们并不关心字典的值,所以我将其设置为"true"。
  4. 为StringBuilder提供不同的方法,而不仅仅是一个字符串将防止不必要的内存分配。如果它只是一个字符串,除非检查了基本情况,否则不要实例化StringBuilder。如果它已经是一个StringBuilder,那么执行替换并返回StringBuilder本身(就像其他StringBuilder方法一样,如params)。
  5. 我把替换字符放在前面,把要检查的字符放在最后。这样,我就可以利用CC_32关键字轻松地传递额外的字符串。但是,如果您喜欢其他订单,则不必这样做。
namespace Test.Extensions
{
    public static class StringExtensions
    {
        public static string ReplaceAll(this string str, char replacementCharacter, params char[] chars)
        {
            if (chars.Length == 0)
                return str;
            if (chars.Length == 1)
                return str.Replace(chars[0], replacementCharacter);
            StringBuilder sb = new StringBuilder(str.Length);
            var searcher = new HybridDictionary(chars.Length);
            for (int i = 0; i < chars.Length; i++)
                searcher[chars[i]] = true;
            foreach (var c in str)
            {
                if (searcher.Contains(c))
                    sb.Append(replacementCharacter);
                else
                    sb.Append(c);
            }
            return sb.ToString();
        }
        public static StringBuilder ReplaceAll(this StringBuilder sb, char replacementCharacter, params char[] chars)
        {
            if (chars.Length == 0)
                return sb;
            if (chars.Length == 1)
                return sb.Replace(chars[0], replacementCharacter);
            var searcher = new HybridDictionary(chars.Length);
            for (int i = 0; i < chars.Length; i++)
                searcher[chars[i]] = true;
            for (int i = 0; i < sb.Length; i++)
            {
                var val = sb[i];
                if (searcher.Contains(val))
                    sb[i] = replacementCharacter;
            }
            return sb;
        }
    }
}

单遍替换,单个或多个值

public static class StringExtensions
{
    public static string ReplaceTokens(this string input, Dictionary<char, string> tokenReplacement)
    {
        if (string.IsNullOrEmpty(input) ||
            tokenReplacement is null ||
            tokenReplacement.Count == 0)
        {
            return input;
        }
        if (tokenReplacement.Count == 1)
        {
            return input.Replace(tokenReplacement.Keys.First().ToString(), tokenReplacement.Values.First());
        }
        StringBuilder output = new StringBuilder(input.Length);
        foreach (char c in input)
        {
            if (tokenReplacement.TryGetValue(c, out string token))
            {
                output.Append(token);
            }
            else
            {
                output.Append(c);
            }
        }
        return output.ToString();
    }
    public static string ReplaceTokens(this string input, char[] tokenReplacement, string replaceWith)
    {
        if (string.IsNullOrEmpty(input) ||
            tokenReplacement is null ||
            tokenReplacement.Length == 0)
        {
            return input;
        }
        if (tokenReplacement.Length == 1)
        {
            return input.Replace(tokenReplacement[0].ToString(), replaceWith ?? String.Empty);
        }
        var splits = input.Split(tokenReplacement);
        return string.Join(replaceWith ?? String.Empty, splits);
    }
}