简体   繁体   中英

Replace one character but not two in a string

I want to replace single occurrences of a character but not two in a string using C#.

For example, I want to replace & by an empty string but not when the ocurrence is && . Another example, a&b&&c would become ab&&c after the replacement.

If I use a regex like &[^&] , it will also match the character after the & and I don't want to replace it.

Another solution I found is to iterate over the string characters.

Do you know a cleaner solution to do that?

您可以匹配&&& (或任意数量的重复),只用空字符串替换单个:

str = Regex.Replace(str, "&+", m => m.Value.Length == 1 ? "" : m.Value);

To only match one & (not preceded or followed by & ), use look-arounds (?<!&) and (?!&) :

(?<!&)&(?!&)

See regex demo

You tried to use a negated character class that still matches a character, and you need to use a look-ahead/look-behind to just check for some character absence/presence, without consuming it.

See regular-expressions.info :

Negative lookahead is indispensable if you want to match something not followed by something else. When explaining character classes , this tutorial explained why you cannot use a negated character class to match a q not followed by a u . Negative lookahead provides the solution: q(?!u) .

Lookbehind has the same effect, but works backwards. It tells the regex engine to temporarily step backwards in the string, to check if the text inside the lookbehind can be matched there. (?<!a)b matches a "b" that is not preceded by an "a" , using negative lookbehind. It doesn't match cab , but matches the b (and only the b ) in bed or debt.

You can use this regex: @"(?<!&)&(?!&)"

var str = Regex.Replace("a&b&&c", @"(?<!&)&(?!&)", "");
Console.WriteLine(str);   // ab&&c

You can go with this:

public static string replacement(string oldString, char charToRemove)
{
    string newString = "";
    bool found = false;
    foreach (char c in oldString)
    {
        if (c == charToRemove && !found)
        {
            found = true;
            continue;
        }
        newString += c;
    }
    return newString;
}

Which is as generic as possible

I would use something like this, which IMO should be better than using Regex :

public static class StringExtensions
{
    public static string ReplaceFirst(this string source, char oldChar, char newChar)
    {
        if (string.IsNullOrEmpty(source)) return source;
        int index = source.IndexOf(oldChar);
        if (index < 0) return source;
        var chars = source.ToCharArray();
        chars[index] = newChar;
        return new string(chars);
    }
}

I'll contribute to this statement from the comments:

in this case, only the substring with odd number of '&' will be replaced by all the "&" except the last "&" . "&&&" would be "&&" and "&&&&" would be "&&&&"

This is a pretty neat solution using balancing groups (though I wouldn't call it particularly clean nor easy to read).

Code:

string str = "11&222&&333&&&44444&&&&55&&&&&";
str = Regex.Replace(str, "&((?:(?<2>&)(?<-2>&)?)*)", "$1$2");

Output:

11222&&333&&44444&&&&55&&&&

ideone demo


  1. It always matches the first & (not captured).
  2. If it's followed by an even number of & , they're matched and stored in $1 . The second group is captured by the first of the pair, but then it's substracted by the second.
  3. However, if there's there's an odd number of of & , the optional group (?<-2>&)? does not match, and the group is not substracted. Then, $2 will capture an extra &

For example, matching the subject "&&&&" , the first char is consumed and it isn't captured ( 1 ). The second and third chars are matched, but $2 is substracted ( 2 ). For the last char, $2 is captured ( 3 ). The last 3 chars were stored in $1 , and there's an extra & in $2 .
Then, the substitution "$1$2" == "&&&&" .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM