Replacing bad characters of a String with bad char

2020-02-07 00:11发布

I just wondered what's the easiest way to replace a string characters that must be replaced subsequently.

For example:

var str = "[Hello World]";
//enclose all occurences of [ and ] with brackets[] 
str = str.Replace("[","[[]").Replace("]","[]]");
  • The desired result: [[]Hello World[]]
  • The actual result: [[[]]Hello World[]]

The reason is obviously the second replace on the already modified string.

So how to replace all occurences of "bad" characters with characters that contain "bad" characters?


A quick measurement of all approaches revealed that the StringBuilder is the most efficient way.

190kb file (all in milliseconds)

  regexTime           40.5065  
  replaceTime         20.8891  
  stringBuilderTime    6.9776

7MB file

  regexTime           1209.3529           
  replaceTime          403.3985   
  stringBuilderTime    175.2583

By the way, the direct StringBuilder approach from John was twice as fast as the Aggregate approach from Sehe.

I've made an extension out of it:

public static String EncloseChars(this string input, char[] charsToEnclose, String leftSide, String rightSide) {
    if (charsToEnclose == null || leftSide == null || rightSide == null)
        throw new ArgumentException("Invalid arguments for EncloseChars", charsToEnclose == null ? "charsToEnclose" : leftSide == null ? "leftSide" : "rightSide");
    Array.Sort(charsToEnclose);
    StringBuilder sb = new StringBuilder();
    foreach (char c in input) {
        if (Array.BinarySearch(charsToEnclose, c) > -1)
            sb.Append(leftSide).Append(c).Append(rightSide);
        else 
            sb.Append(c);
    }
    return sb.ToString();
}

"[Hello World]".EncloseChars(new char[]{'[', ']'},"[","]");

6条回答
Anthone
2楼-- · 2020-02-07 00:49

How about:

char[] replacedChars = str.SelectMany(ch => 
                                     (ch == '[' ? new char[] {'[', '[', ']'} :
                                     (ch == ']' ? new char[] {'[', ']', ']'} : 
                                     new char[] {ch}))).ToArray();
string replaced = new string(replacedChars);

Note that this avoids the multiple loops issue but creates at least as many arrays as there are characters in the input string so it might not be optimal in terms of performance.

查看更多
够拽才男人
3楼-- · 2020-02-07 00:49

I had the exact same problem, so I made a helper function to do just that

    protected string ReplaceUsingDictionary(string subject, Dictionary<string,string> pairs)
    {
        StringBuilder sb = new StringBuilder(subject);

        sb.Replace("{", "{{").Replace("}", "}}");

        int i=0;
        foreach (string key in pairs.Keys.ToArray())
        {
            sb.Replace(
                key.Replace("{", "{{").Replace("}", "}}"), 
                "{" + i + "}"
            );

            i++;
        }

        return string.Format(sb.ToString(), pairs.Values.ToArray());
    }

// usage
Dictionary<string, string> replacements = new Dictionary<string, string>();
replacements["["] = "[[]";
replacements["]"] = "[]]";

string mystr = ReplaceWithDictionary("[HelloWorld]", replacements); // returns [[]HelloWorld[]]
查看更多
再贱就再见
4楼-- · 2020-02-07 01:07

What about this elegant regular expression approach:

Regex.Replace("[Hello World]", @"[\[\]]", "[$0]");

Unit test it?

[TestMethod]
public void UnitTestThat()
{
    Assert.AreEqual(@"[[]Hello World[]]", Regex.Replace("[Hello World]", @"[\[\]]", "[$0]"));
}

Test passed


Edit @JohnMcGrant

Here is a slightly less inefficient version of your code, which has, by the way, exactly the same behaviour as the above regex:

string result = input.Aggregate(new StringBuilder(), (a, c) =>
    -1 != "[]".IndexOf(c) ? a.AppendFormat("[{0}]", c) : a.Append(c)).ToString();
查看更多
▲ chillily
5楼-- · 2020-02-07 01:08

Here's a very uncool way to do it. But it has the advantage of being pretty close to foolproof, I think, and not using regex (in case you'd rather not use regex).

StringBuilder sb = new StringBuilder();
foreach (char c in str.ToCharArray()) {
    if (c == '[' || c == ']') {
        sb.Append('[' + c + ']');
    }
    else {
        sb.Append(c);
    }
}
string result = sb.ToString();
查看更多
戒情不戒烟
6楼-- · 2020-02-07 01:14

What about:

str = str.Replace("[", "$1[$2")
         .Replace("]", "$1]$2")
         .Replace("$1", "[")
         .Replace("$2", "]");
查看更多
狗以群分
7楼-- · 2020-02-07 01:16
    StringBuilder result = new StringBuilder();

    foreach (Char singleCharacter in str)
    {
        result.Append(singleCharacter.Equals('[') ? "[[]" : singleCharacter.Equals(']') ? "[]]" : singleCharacter.ToString());
    }

    str = result.ToString();
查看更多
登录 后发表回答