String.Replace ignorieren Groß- / Kleinschreibung


214

Ich habe eine Zeichenfolge namens "Hallo Welt"

Ich muss das Wort "Welt" durch "csharp" ersetzen.

dafür benutze ich:

string.Replace("World", "csharp");

Infolgedessen wird die Zeichenfolge jedoch nicht ersetzt. Der Grund ist die Groß- und Kleinschreibung. Die ursprüngliche Zeichenfolge enthält "Welt", während ich versuche, "Welt" zu ersetzen.

Gibt es eine Möglichkeit, diese Fallempfindlichkeit in der string.Replace-Methode zu vermeiden?


5
Hier finden Sie ein ähnliches
Michał Kuliński

Antworten:


309

Sie können einen Regex verwenden und eine Ersetzung ohne Berücksichtigung der Groß- und Kleinschreibung durchführen:

class Program
{
    static void Main()
    {
        string input = "hello WoRlD";
        string result = 
           Regex.Replace(input, "world", "csharp", RegexOptions.IgnoreCase);
        Console.WriteLine(result); // prints "hello csharp"
    }
}

19
Funktioniert nicht mit Regex-Sprachelementen , daher ist es keine universelle Methode. Die Antwort von Steve B ist richtig.
AsValeO

1
Sie sollten also besser nicht schreiben hello. world?oder etwas anderes, das Regex-Operatoren enthält.
Sebastian Mach

Nur für den Fall, dass jemand nicht dazu neigte, weiterzulesen, war dies die akzeptierte Antwort im Jahr 2011 und hat eine große Anzahl von Stimmen. Dies funktioniert einwandfrei, wenn Sie nur alphanumerische Zeichen ersetzen müssen. Wenn Sie jedoch Satzzeichen ersetzen müssen, können große Probleme auftreten. Oleg Zarevennyis Antwort ist überlegen, hat aber nur eine geringe Anzahl von Stimmen, da sie im Jahr 2017 veröffentlicht wurde.
Tony Pulokas

115
var search = "world";
var replacement = "csharp";
string result = Regex.Replace(
    stringToLookInto,
    Regex.Escape(search), 
    replacement.Replace("$","$$"), 
    RegexOptions.IgnoreCase
);

Die Regex.Escape ist nützlich , wenn Sie auf Benutzereingaben verlassen die Dose enthält Regex Sprachelemente

Aktualisieren

Dank Kommentaren müssen Sie der Ersatzzeichenfolge eigentlich nicht entkommen.

Hier ist eine kleine Geige, die den Code testet :

using System;
using System.Text.RegularExpressions;           
public class Program
{
    public static void Main()
    {

        var tests = new[] {
            new { Input="abcdef", Search="abc", Replacement="xyz", Expected="xyzdef" },
            new { Input="ABCdef", Search="abc", Replacement="xyz", Expected="xyzdef" },
            new { Input="A*BCdef", Search="a*bc", Replacement="xyz", Expected="xyzdef" },
            new { Input="abcdef", Search="abc", Replacement="x*yz", Expected="x*yzdef" },       
            new { Input="abcdef", Search="abc", Replacement="$", Expected="$def" },
        };


        foreach(var test in tests){
            var result = ReplaceCaseInsensitive(test.Input, test.Search, test.Replacement);

            Console.WriteLine(
                "Success: {0}, Actual: {1}, {2}",
                result == test.Expected,
                result,
                test
            );

        }


    }

    private static string ReplaceCaseInsensitive(string input, string search, string replacement){
        string result = Regex.Replace(
            input,
            Regex.Escape(search), 
            replacement.Replace("$","$$"), 
            RegexOptions.IgnoreCase
        );
        return result;
    }
}

Seine Ausgabe ist:

Success: True, Actual: xyzdef, { Input = abcdef, Search = abc, Replacement = xyz, Expected = xyzdef } 
Success: True, Actual: xyzdef, { Input = ABCdef, Search = abc, Replacement = xyz, Expected = xyzdef }
Success: True, Actual: xyzdef, { Input = A*BCdef, Search = a*bc, Replacement = xyz, Expected = xyzdef } 
Success: True, Actual: x*yzdef, { Input = abcdef, Search = abc, Replacement = x*yz, Expected = x*yzdef} 
Success: True, Actual: $def, { Input = abcdef, Search = abc, Replacement = $, Expected = $def }

2
Diese Methode schlägt fehl, wenn ersetzt = "! @ # $% ^ & * ()" Sie erhalten "! @ \ # \ $% \ ^ & * ()" Stattdessen ersetzt wird.
Kcoder

2
Das zweite Regex.Escapeist schlecht, es wird Sonderzeichen Backslashes voranstellen. Der beste Weg scheint .Replace ("$", "$$") zu sein, was ein bisschen dumm ist ( stackoverflow.com/a/10078353 ).
Danny Tuppeny

1
@dannyTuppeny: Sie haben Recht ... Ich habe die Antwort entsprechend aktualisiert
Steve B

54

2,5X SCHNELLER und EFFEKTIVSTE Methode als die Methoden anderer regulärer Ausdrücke:

/// <summary>
/// Returns a new string in which all occurrences of a specified string in the current instance are replaced with another 
/// specified string according the type of search to use for the specified string.
/// </summary>
/// <param name="str">The string performing the replace method.</param>
/// <param name="oldValue">The string to be replaced.</param>
/// <param name="newValue">The string replace all occurrences of <paramref name="oldValue"/>. 
/// If value is equal to <c>null</c>, than all occurrences of <paramref name="oldValue"/> will be removed from the <paramref name="str"/>.</param>
/// <param name="comparisonType">One of the enumeration values that specifies the rules for the search.</param>
/// <returns>A string that is equivalent to the current string except that all instances of <paramref name="oldValue"/> are replaced with <paramref name="newValue"/>. 
/// If <paramref name="oldValue"/> is not found in the current instance, the method returns the current instance unchanged.</returns>
[DebuggerStepThrough]
public static string Replace(this string str,
    string oldValue, string @newValue,
    StringComparison comparisonType)
{

    // Check inputs.
    if (str == null)
    {
        // Same as original .NET C# string.Replace behavior.
        throw new ArgumentNullException(nameof(str));
    }
    if (str.Length == 0)
    {
        // Same as original .NET C# string.Replace behavior.
        return str;
    }
    if (oldValue == null)
    {
        // Same as original .NET C# string.Replace behavior.
        throw new ArgumentNullException(nameof(oldValue));
    }
    if (oldValue.Length == 0)
    {
        // Same as original .NET C# string.Replace behavior.
        throw new ArgumentException("String cannot be of zero length.");
    }


    //if (oldValue.Equals(newValue, comparisonType))
    //{
    //This condition has no sense
    //It will prevent method from replacesing: "Example", "ExAmPlE", "EXAMPLE" to "example"
    //return str;
    //}



    // Prepare string builder for storing the processed string.
    // Note: StringBuilder has a better performance than String by 30-40%.
    StringBuilder resultStringBuilder = new StringBuilder(str.Length);



    // Analyze the replacement: replace or remove.
    bool isReplacementNullOrEmpty = string.IsNullOrEmpty(@newValue);



    // Replace all values.
    const int valueNotFound = -1;
    int foundAt;
    int startSearchFromIndex = 0;
    while ((foundAt = str.IndexOf(oldValue, startSearchFromIndex, comparisonType)) != valueNotFound)
    {

        // Append all characters until the found replacement.
        int @charsUntilReplacment = foundAt - startSearchFromIndex;
        bool isNothingToAppend = @charsUntilReplacment == 0;
        if (!isNothingToAppend)
        {
            resultStringBuilder.Append(str, startSearchFromIndex, @charsUntilReplacment);
        }



        // Process the replacement.
        if (!isReplacementNullOrEmpty)
        {
            resultStringBuilder.Append(@newValue);
        }


        // Prepare start index for the next search.
        // This needed to prevent infinite loop, otherwise method always start search 
        // from the start of the string. For example: if an oldValue == "EXAMPLE", newValue == "example"
        // and comparisonType == "any ignore case" will conquer to replacing:
        // "EXAMPLE" to "example" to "example" to "example" … infinite loop.
        startSearchFromIndex = foundAt + oldValue.Length;
        if (startSearchFromIndex == str.Length)
        {
            // It is end of the input string: no more space for the next search.
            // The input string ends with a value that has already been replaced. 
            // Therefore, the string builder with the result is complete and no further action is required.
            return resultStringBuilder.ToString();
        }
    }


    // Append the last part to the result.
    int @charsUntilStringEnd = str.Length - startSearchFromIndex;
    resultStringBuilder.Append(str, startSearchFromIndex, @charsUntilStringEnd);


    return resultStringBuilder.ToString();

}

Hinweis: Groß- / Kleinschreibung ignorieren == StringComparison.OrdinalIgnoreCaseals Parameter für StringComparison comparisonType. Dies ist die schnellste Methode, bei der die Groß- und Kleinschreibung nicht berücksichtigt wird, um alle Werte zu ersetzen.


Vorteile dieser Methode:

  • Hohe CPU- und MEMORY-Effizienz;
  • Es ist die schnellste Lösung, 2,5-mal schneller als andere Methoden mit regulären Ausdrücken (Beweis am Ende);
  • Geeignet zum Entfernen von Teilen aus der Eingabezeichenfolge (eingestellt newValueauf null ), optimiert dafür;
  • Entspricht dem Original .NET C # string.Replace -Verhalten, gleiche Ausnahmen;
  • Gut kommentiert, leicht zu verstehen;
  • Einfacher - keine regulären Ausdrücke. Reguläre Ausdrücke sind aufgrund ihrer Vielseitigkeit immer langsamer (sogar kompiliert).
  • Diese Methode ist gut getestet und es gibt keine versteckten Fehler wie Endlosschleifen in anderen Lösungen, selbst wenn sie hoch bewertet werden:

@AsValeO: Funktioniert nicht mit Regex-Sprachelementen, ist also keine universelle Methode

@ Mike Stillion: Es gibt ein Problem mit diesem Code. Wenn der neue Text eine Obermenge des alten Textes ist, kann dies zu einer Endlosschleife führen.


Benchmark- sicher : Diese Lösung ist 2,59- mal schneller als Regex von @Steve B., Code:

// Results:
// 1/2. Regular expression solution: 4486 milliseconds
// 2/2. Current solution: 1727 milliseconds — 2.59X times FASTER! than regex!

// Notes: the test was started 5 times, the result is an average; release build.

const int benchmarkIterations = 1000000;
const string sourceString = "aaaaddsdsdsdsdsd";
const string oldValue = "D";
const string newValue = "Fod";
long totalLenght = 0;

Stopwatch regexStopwatch = Stopwatch.StartNew();
string tempString1;
for (int i = 0; i < benchmarkIterations; i++)
{
    tempString1 = sourceString;
    tempString1 = ReplaceCaseInsensitive(tempString1, oldValue, newValue);

    totalLenght = totalLenght + tempString1.Length;
}
regexStopwatch.Stop();



Stopwatch currentSolutionStopwatch = Stopwatch.StartNew();
string tempString2;
for (int i = 0; i < benchmarkIterations; i++)
{
    tempString2 = sourceString;
    tempString2 = tempString2.Replace(oldValue, newValue,
        StringComparison.OrdinalIgnoreCase);

    totalLenght = totalLenght + tempString2.Length;
}
currentSolutionStopwatch.Stop();

Ursprüngliche Idee - @ Darky711; danke @MinerR für StringBuilder.


5
Ich wette, Sie können dies mit einem StringBuilder anstelle eines Strings noch schneller machen.
MineR

1
@MineR Sie haben Recht, ich habe ursprünglich gerade die @ Darky711-Lösung ohne Endlosschleife aktualisiert, also habe ich die verwendet String. Allerdings ist das StringBuilderwirklich 30-40% schneller als das String. Ich habe die Lösung aktualisiert. Danke;)
Oleg Zarevennyi

2
Interessanter Ansatz. Wahrscheinlich der bessere (besser als meiner :)), wenn es auf die Leistung ankommt. In der Regel eine Methode zum Hinzufügen zu einer gemeinsamen gemeinsam genutzten Codebibliothek.
Steve B

2
Die Verwendung von 'nameof'-Ausdrücken macht dies nur für C # 6.0 und höher gültig. Wenn Sie sich in VS2013 befinden, können Sie es verwenden, indem Sie einfach die Operanden in den Ausnahmen löschen.
LanchPad

Für das auskommentierte "// if (oldValue.Equals (newValue, compareType))" ersetzen Sie compareType durch StringComparison.Ordinal?
Roger Willcocks

31

Erweiterungen erleichtern unser Leben:

static public class StringExtensions
{
    static public string ReplaceInsensitive(this string str, string from, string to)
    {
        str = Regex.Replace(str, from, to, RegexOptions.IgnoreCase);
        return str;
    }
}

10
Und Flucht macht unser Leben weniger fehlerhaft :-) return Regex.Replace (Eingabe, Regex.Escape (Suche), Ersetzung.Replace ("$", "$$"), RegexOptions.IgnoreCase);
Vman

29

Viele Vorschläge mit Regex. Wie wäre es mit dieser Erweiterungsmethode ohne:

public static string Replace(this string str, string old, string @new, StringComparison comparison)
{
    @new = @new ?? "";
    if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(old) || old.Equals(@new, comparison))
        return str;
    int foundAt = 0;
    while ((foundAt = str.IndexOf(old, foundAt, comparison)) != -1)
    {
        str = str.Remove(foundAt, old.Length).Insert(foundAt, @new);
        foundAt += @new.Length;
    }
    return str;
}

Beachten Sie, dass das Vergleichsargument nicht für den eigentlichen Austausch verwendet wird (es wird immer ohne Berücksichtigung der Groß- und Kleinschreibung berücksichtigt)
Bolo

2
Es gibt ein Problem mit diesem Code. Wenn der Text in neu eine Obermenge des Textes in alt ist , kann dies eine Endlosschleife erzeugen. Sobald bei FoundAt new eingefügt wurde , muss der Wert von FoundAt um die Länge von new erweitert werden .
Mike Stillion

comparisonParameter sollte in IndexOfanstelle vonStringComparison.CurrentCultureIgnoreCase
Maxence

@Bolo Ich habe es bearbeitet, um das Vergleichsargument zu verwenden (es kann etwas dauern, bis es einer Peer-Review unterzogen wird).
Bradlis7

2
Ich würde diese Bedingung auch für die Rückgabe der neuen Zeichenfolge trennen : if(old.Equals(@new, comparison)) return @new;, da sich die neue Zeichenfolge in Groß- / Kleinbuchstaben unterscheiden kann.
sɐunıɔ ןɐ qɐp

13

Sie können den Microsoft.VisualBasic- Namespace verwenden, um diese Hilfsfunktion zu finden:

Replace(sourceString, "replacethis", "withthis", , , CompareMethod.Text)

Ich war stolz auf meine Antwort, bis ich sah, dass dies eine bessere Antwort ist, weil sie eingebaut ist. Beispiel: Strings.Replace ("TeStInG123", "t", "z", 1, -1, CompareMethod.Text) gibt "zurück. zeSzInG123 "
Bolo

Warnung, Strings.Replace gibt null zurück, wenn die gesuchte Zeichenfolge eine leere Zeichenfolge ist.
Mafu Josh

1
In .Net 4.7.2 müssen Sie einen Verweis auf Microsoft.VisualBasic hinzufügen, damit dies funktioniert. In .Net Core scheint die Microsoft.VisualBasic.Strings-Klasse (in Version 10.3.0 sowieso) die Ersetzungsfunktion nicht zu implementieren. Dies funktioniert auch in Powershell, wenn Sie zuerst die Klasse -AssemblyName Microsoft.VisualBasic hinzufügen.
Prof. Von Lemongargle

6

( Bearbeitet: Das Problem mit dem "nackten Link" war mir nicht bekannt. Tut mir leid.)

Von hier genommen :

string myString = "find Me and replace ME";
string strReplace = "me";
myString = Regex.Replace(myString, "me", strReplace, RegexOptions.IgnoreCase);

Sie scheinen nicht der erste zu sein, der sich über das Fehlen einer Zeichenfolge ohne Berücksichtigung der Groß- und Kleinschreibung beschwert.


5

Die Antwort von @ Darky711 wurde geändert, um den übergebenen Vergleichstyp zu verwenden und mit dem Framework übereinzustimmen. Ersetzen Sie Namens- und XML-Kommentare so genau wie möglich.

/// <summary>
/// Returns a new string in which all occurrences of a specified string in the current instance are replaced with another specified string.
/// </summary>
/// <param name="str">The string performing the replace method.</param>
/// <param name="oldValue">The string to be replaced.</param>
/// <param name="newValue">The string replace all occurrances of oldValue.</param>
/// <param name="comparisonType">Type of the comparison.</param>
/// <returns></returns>
public static string Replace(this string str, string oldValue, string @newValue, StringComparison comparisonType)
{
    @newValue = @newValue ?? string.Empty;
    if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(oldValue) || oldValue.Equals(@newValue, comparisonType))
    {
        return str;
    }
    int foundAt;
    while ((foundAt = str.IndexOf(oldValue, 0, comparisonType)) != -1)
    {
        str = str.Remove(foundAt, oldValue.Length).Insert(foundAt, @newValue);
    }
    return str;
}

2

Ich habe eine Erweiterungsmethode geschrieben:

public static string ReplaceIgnoreCase(this string source, string oldVale, string newVale)
    {
        if (source.IsNullOrEmpty() || oldVale.IsNullOrEmpty())
            return source;

        var stringBuilder = new StringBuilder();
        string result = source;

        int index = result.IndexOf(oldVale, StringComparison.InvariantCultureIgnoreCase);

        while (index >= 0)
        {
            if (index > 0)
                stringBuilder.Append(result.Substring(0, index));

            if (newVale.IsNullOrEmpty().IsNot())
                stringBuilder.Append(newVale);

            stringBuilder.Append(result.Substring(index + oldVale.Length));

            result = stringBuilder.ToString();

            index = result.IndexOf(oldVale, StringComparison.InvariantCultureIgnoreCase);
        }

        return result;
    }

Ich verwende zwei zusätzliche Erweiterungsmethoden für die vorherige Erweiterungsmethode:

    public static bool IsNullOrEmpty(this string value)
    {
        return string.IsNullOrEmpty(value);
    }

    public static bool IsNot(this bool val)
    {
        return val == false;
    }

2
Upvoted. Aber IsNotnimmt Erweiterungen zu ernst :)
Nawfal

Enttäuschenderweise funktioniert dies nicht in allen Situationen. Ich habe einen eindeutigen Namen übergeben, der angehängt wird, bis die Zeichenfolge eine Million Zeichen lang ist und dann nicht mehr
Bbb

Die unten angebotene Alternative hat mein Problem behoben
Bbb

Ich mag.IsNot
ttugates

1

Erweitern Sie die Antwort von Petrucio mit Regex.Escapein der Suchzeichenfolge und entkommen Sie der übereinstimmenden Gruppe, wie in Steve Bs Antwort vorgeschlagen (und einige geringfügige Änderungen an meinem Geschmack):

public static class StringExtensions
{
    public static string ReplaceIgnoreCase(this string str, string from, string to)
    {
        return Regex.Replace(str, Regex.Escape(from), to.Replace("$", "$$"), RegexOptions.IgnoreCase);
    }
}

Welches wird die folgenden erwarteten Ergebnisse produzieren:

Console.WriteLine("(heLLo) wOrld".ReplaceIgnoreCase("(hello) world", "Hi $1 Universe")); // Hi $1 Universe
Console.WriteLine("heLLo wOrld".ReplaceIgnoreCase("(hello) world", "Hi $1 Universe"));   // heLLo wOrld

Ohne die Escape-Aktionen würden Sie jedoch Folgendes erhalten, was kein erwartetes Verhalten von einem String.Replaceist, bei dem nur die Groß- und Kleinschreibung nicht berücksichtigt wird:

Console.WriteLine("(heLLo) wOrld".ReplaceIgnoreCase_NoEscaping("(hello) world", "Hi $1 Universe")); // (heLLo) wOrld
Console.WriteLine("heLLo wOrld".ReplaceIgnoreCase_NoEscaping("(hello) world", "Hi $1 Universe"));   // Hi heLLo Universe

1

Funktioniert das nicht: Ich kann mir nichts anderes vorstellen, das viel schneller oder einfacher ist.

public static class ExtensionMethodsString
{
    public static string Replace(this String thisString, string oldValue, string newValue, StringComparison stringComparison)
    {
        string working = thisString;
        int index = working.IndexOf(oldValue, stringComparison);
        while (index != -1)
        {
            working = working.Remove(index, oldValue.Length);
            working = working.Insert(index, newValue);
            index = index + newValue.Length;
            index = working.IndexOf(oldValue, index, stringComparison);
        }
        return working;
    }
}

Ich weiß nicht, ob es schneller ist, aber es ist prägnant, verwendet keinen Regex-Overhead und potenzielle Probleme und verwendet den integrierten StringComparison.
fvlinden

0

Die folgende Funktion besteht darin, alle Übereinstimmungswörter wie diese aus dem Zeichenfolgensatz zu entfernen. Von Ravikant Sonare.

private static void myfun()
{
    string mystring = "thiTHISThiss This THIS THis tThishiThiss. Box";
    var regex = new Regex("this", RegexOptions.IgnoreCase);
    mystring = regex.Replace(mystring, "");
    string[] str = mystring.Split(' ');
    for (int i = 0; i < str.Length; i++)
    {
        if (regex.IsMatch(str[i].ToString()))
        {
            mystring = mystring.Replace(str[i].ToString(), string.Empty);

        }
    }
    Console.WriteLine(mystring);
}

Diese Funktion ersetzt alle Zeichenfolgen aus dem Zeichenfolgensatz ... von Ravikant Sonare,
Ravikant Sonare

0

Bei Verwendung der @ George Batalov-Lösung hatte ich ein Problem bei der Verwendung des folgenden Beispiels

Zeichenfolge original = "bla, DC = bleh, DC = blih, DC = bloh, DC = com"; Zeichenfolge ersetzt = original.ReplaceIgnoreCase (", DC =", ".")

Unten ist, wie ich seine Erweiterung umgeschrieben habe

public static string ReplaceIgnoreCase(this string source, string oldVale, 
string newVale)
    {
        if (source.IsNullOrEmpty() || oldVale.IsNullOrEmpty())
            return source;

        var stringBuilder = new StringBuilder();
        string result = source;

        int index = result.IndexOf(oldVale, StringComparison.InvariantCultureIgnoreCase);
        bool initialRun = true;

        while (index >= 0)
        {
            string substr = result.Substring(0, index);
            substr = substr + newVale;
            result = result.Remove(0, index);
            result = result.Remove(0, oldVale.Length);

            stringBuilder.Append(substr);

            index = result.IndexOf(oldVale, StringComparison.InvariantCultureIgnoreCase);
        }

        if (result.Length > 0)
        {
            stringBuilder.Append(result);
        }

        return stringBuilder.ToString();
    }

0

Im Folgenden finden Sie die Alternative zum Ersetzen von Zeichenfolgen, bei denen die Groß- und Kleinschreibung ignoriert wird

String thisString = "hello world"; 
String replaceString = "World";

//thisString.Replace("World", "csharp"); 
//below is the alternative to replace string ignoring character case

int start = StringUtils.indexOfIgnoreCase(thisString,replaceString);
String searchKey = thisString.substring(start, start+replaceString.length());
thisString= thisString.replaceAll(searchKey ,replaceString );
System.out.println(thisString);

//prints hello World

0

Sie können auch die RegexKlasse ausprobieren .

var regex = new Regex( "camel", RegexOptions.IgnoreCase ); var newSentence = regex.Replace( sentence, "horse" );


-3

Ich bevorzuge dies - "Hallo Welt" .ToLower (). Ersetzen ("Welt", "csharp");


1
Dadurch wird alles in Kleinbuchstaben geschrieben, auch Wörter, die nicht ersetzt werden sollten.
JJJ

Natürlich können Sie dies nur verwenden, wenn Sie sich nicht um den Fall kümmern.
Harshal
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.