it-swarm.com.de

Gibt es eine Alternative zu string.Replace, bei der die Groß- und Kleinschreibung nicht berücksichtigt wird?

Ich muss einen String durchsuchen und alle Vorkommen von %FirstName% und %PolicyAmount% durch einen Wert ersetzen, der aus einer Datenbank abgerufen wird. Das Problem ist, dass die Großschreibung von FirstName variiert. Das hindert mich daran, die String.Replace()-Methode zu verwenden. Ich habe Webseiten zu diesem Thema gesehen

Regex.Replace(strInput, strToken, strReplaceWith, RegexOptions.IgnoreCase);

Wenn ich jedoch versuche, %PolicyAmount% durch $0 zu ersetzen, wird der Ersatz aus irgendeinem Grund niemals durchgeführt. Ich gehe davon aus, dass es etwas damit zu tun hat, dass das Dollarzeichen ein reserviertes Zeichen in Regex ist. 

Gibt es eine andere Methode, die ich verwenden kann, ohne die Eingabe zu bereinigen, um reguläre Ausdrücke zu behandeln?

293
Aheho

Von MSDN
$ 0 - "Ersetzt die letzte Teilzeichenfolge durch Gruppennummer (Dezimalzahl)."

In .NET reguläre Ausdrücke ist Gruppe 0 immer die gesamte Übereinstimmung. Für ein buchstäbliches $ müssen Sie

string value = Regex.Replace("%PolicyAmount%", "%PolicyAmount%", @"$$0", RegexOptions.IgnoreCase);
127
Todd White

Scheint, als wäre string.Replace solleine Überladung haben, die ein StringComparison-Argument benötigt. Da dies nicht der Fall ist, können Sie Folgendes versuchen:

public static string ReplaceString(string str, string oldValue, string newValue, StringComparison comparison)
{
    StringBuilder sb = new StringBuilder();

    int previousIndex = 0;
    int index = str.IndexOf(oldValue, comparison);
    while (index != -1)
    {
        sb.Append(str.Substring(previousIndex, index - previousIndex));
        sb.Append(newValue);
        index += oldValue.Length;

        previousIndex = index;
        index = str.IndexOf(oldValue, index, comparison);
    }
    sb.Append(str.Substring(previousIndex));

    return sb.ToString();
}
291
C. Dragon 76

Eine verwirrende Gruppe von Antworten, zum Teil, weil der Titel der Frage tatsächlich viel größer ist als die spezifische Frage, die gestellt wird. Nach dem Durchlesen bin ich mir nicht sicher, ob eine Antwort ein paar Änderungen entfernt ist, um all die guten Sachen hier zu assimilieren, also dachte ich mir, ich würde versuchen, es zusammenzufassen.

Hier ist eine Erweiterungsmethode, die meines Erachtens die hier erwähnten Fallstricke vermeidet und die am weitesten verbreitete Lösung bietet.

public static string ReplaceCaseInsensitiveFind(this string str, string findMe,
    string newValue)
{
    return Regex.Replace(str,
        Regex.Escape(findMe),
        Regex.Replace(newValue, "\\$[0-9]+", @"$$$0"),
        RegexOptions.IgnoreCase);
}

Damit...

Leider ist @ HAs Kommentar, dass Sie Escape alle drei müssen, nicht korrekt . Der Anfangswert und newValue müssen nicht sein.

Hinweis: Sie müssen jedoch $ In den neuen Wert, den Sie einfügen, einschließen wenn sie Teil eines scheinbar "erfassten Wertes" sind . So die drei Dollarzeichen im Regex.Replace im Regex.Replace. Ohne das bricht so etwas ...

"This is HIS fork, hIs spoon, hissssssss knife.".ReplaceCaseInsensitiveFind("his", @"he$0r")

Hier ist der Fehler:

An unhandled exception of type 'System.ArgumentException' occurred in System.dll

Additional information: parsing "The\hisr\ is\ he\HISr\ fork,\ he\hIsr\ spoon,\ he\hisrsssssss\ knife\." - Unrecognized escape sequence \h.

Weißt du was? Ich weiß, Leute, die mit Regex vertraut sind, haben das Gefühl, dass ihre Verwendung Fehler vermeidet, aber ich bin oft immer noch parteiisch gegenüber Byte-Schnüfflern (aber erst, nachdem ich Spolsky on encodings gelesen habe), um absolut zu sein Sie erhalten genau das, was Sie für wichtige Anwendungsfälle vorgesehen haben. Erinnert mich ein wenig an Crockford bei " nsichere reguläre Ausdrücke ". Zu oft schreiben wir reguläre Ausdrücke, die das zulassen, was wir wollen (wenn wir Glück haben), aber versehentlich mehr zulassen (z. B. Ist $10 Wirklich ein gültiger "Erfassungswert" -String in meinem regulären Neuwert-Ausdruck oben?) wir waren nicht nachdenklich genug. Beide Methoden haben einen Wert, und beide fördern verschiedene Arten von unbeabsichtigten Fehlern. Es ist oft leicht, die Komplexität zu unterschätzen.

Das seltsame Entkommen von $ (Und das Entkommen von Regex.Escape Nicht den erfassten Wertmustern wie $0, Wie ich es bei Ersatzwerten erwartet hätte) hat mich eine Weile verrückt gemacht. Programmierung ist schwierig (c) 1842

41
ruffin

Die einfachste Methode ist einfach die Verwendung der Replace-Methode, die mit .Net ausgeliefert wird und seit .Net 1.0 verfügbar ist:

string res = Microsoft.VisualBasic.Strings.Replace(res, 
                                   "%PolicyAmount%", 
                                   "$0", 
                                   Compare: Microsoft.VisualBasic.CompareMethod.Text);

Um diese Methode verwenden zu können, müssen Sie einen Verweis auf Microsoft.VisualBasic zusammenfügen. Diese Assembly ist ein Standardbestandteil der .NET-Laufzeitumgebung. Sie ist kein zusätzlicher Download oder als veraltet gekennzeichnet.

30
Clever Human

Hier ist eine Erweiterungsmethode. Nicht sicher, wo ich es gefunden habe.

public static class StringExtensions
{
    public static string Replace(this string originalString, string oldValue, string newValue, StringComparison comparisonType)
    {
        int startIndex = 0;
        while (true)
        {
            startIndex = originalString.IndexOf(oldValue, startIndex, comparisonType);
            if (startIndex == -1)
                break;

            originalString = originalString.Substring(0, startIndex) + newValue + originalString.Substring(startIndex + oldValue.Length);

            startIndex += newValue.Length;
        }

        return originalString;
    }

}
30
rboarman
    /// <summary>
    /// A case insenstive replace function.
    /// </summary>
    /// <param name="originalString">The string to examine.(HayStack)</param>
    /// <param name="oldValue">The value to replace.(Needle)</param>
    /// <param name="newValue">The new value to be inserted</param>
    /// <returns>A string</returns>
    public static string CaseInsenstiveReplace(string originalString, string oldValue, string newValue)
    {
        Regex regEx = new Regex(oldValue,
           RegexOptions.IgnoreCase | RegexOptions.Multiline);
        return regEx.Replace(originalString, newValue);
    }
10
Karl Glennon

Inspiriert durch die Antwort von cfeduke habe ich diese Funktion erstellt, die IndexOf verwendet, um den alten Wert in der Zeichenfolge zu finden und ihn dann durch den neuen Wert zu ersetzen. Ich habe dies in einem SSIS-Skript verwendet, das Millionen von Zeilen verarbeitet, und die Regex-Methode war viel langsamer als diese.

public static string ReplaceCaseInsensitive(this string str, string oldValue, string newValue)
{
    int prevPos = 0;
    string retval = str;
    // find the first occurence of oldValue
    int pos = retval.IndexOf(oldValue, StringComparison.InvariantCultureIgnoreCase);

    while (pos > -1)
    {
        // remove oldValue from the string
        retval = retval.Remove(pos, oldValue.Length);

        // insert newValue in it's place
        retval = retval.Insert(pos, newValue);

        // check if oldValue is found further down
        prevPos = pos + newValue.Length;
        pos = retval.IndexOf(oldValue, prevPos, StringComparison.InvariantCultureIgnoreCase);
    }

    return retval;
}
8
JeroenV

Erweitern Sie die beliebte Antwort von C. Dragon 76 , indem Sie ihren Code in eine Erweiterung umwandeln, die die Standardmethode Replace überlädt.

public static class StringExtensions
{
    public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison)
    {
        StringBuilder sb = new StringBuilder();

        int previousIndex = 0;
        int index = str.IndexOf(oldValue, comparison);
        while (index != -1)
        {
            sb.Append(str.Substring(previousIndex, index - previousIndex));
            sb.Append(newValue);
            index += oldValue.Length;

            previousIndex = index;
            index = str.IndexOf(oldValue, index, comparison);
        }
        sb.Append(str.Substring(previousIndex));
        return sb.ToString();
     }
}
6
Chad Kuehn

Basierend auf Jeff Reddys Antwort mit einigen Optimierungen und Validierungen:

public static string Replace(string str, string oldValue, string newValue, StringComparison comparison)
{
    if (oldValue == null)
        throw new ArgumentNullException("oldValue");
    if (oldValue.Length == 0)
        throw new ArgumentException("String cannot be of zero length.", "oldValue");

    StringBuilder sb = null;

    int startIndex = 0;
    int foundIndex = str.IndexOf(oldValue, comparison);
    while (foundIndex != -1)
    {
        if (sb == null)
            sb = new StringBuilder(str.Length + (newValue != null ? Math.Max(0, 5 * (newValue.Length - oldValue.Length)) : 0));
        sb.Append(str, startIndex, foundIndex - startIndex);
        sb.Append(newValue);

        startIndex = foundIndex + oldValue.Length;
        foundIndex = str.IndexOf(oldValue, startIndex, comparison);
    }

    if (startIndex == 0)
        return str;
    sb.Append(str, startIndex, str.Length - startIndex);
    return sb.ToString();
}
3
Mark Cranness

eine Version, die der von C. Dragon ähnelt, aber wenn Sie nur einen einzigen Ersatz benötigen:

int n = myText.IndexOf(oldValue, System.StringComparison.InvariantCultureIgnoreCase);
if (n >= 0)
{
    myText = myText.Substring(0, n)
        + newValue
        + myText.Substring(n + oldValue.Length);
}
2
Allanrbo

Hier ist eine weitere Option zum Ausführen von Regex-Ersetzungen, da nicht viele Leute bemerken, dass die Übereinstimmungen den Ort innerhalb der Zeichenfolge enthalten:

    public static string ReplaceCaseInsensative( this string s, string oldValue, string newValue ) {
        var sb = new StringBuilder(s);
        int offset = oldValue.Length - newValue.Length;
        int matchNo = 0;
        foreach (Match match in Regex.Matches(s, Regex.Escape(oldValue), RegexOptions.IgnoreCase))
        {
            sb.Remove(match.Index - (offset * matchNo), match.Length).Insert(match.Index - (offset * matchNo), newValue);
            matchNo++;
        }
        return sb.ToString();
    }
1
Brandon
Regex.Replace(strInput, strToken.Replace("$", "[$]"), strReplaceWith, RegexOptions.IgnoreCase);
0
Joel Coehoorn

(Da macht jeder einen Schuss darauf). Hier ist meine Version (mit Nullprüfungen und korrekter Eingabe und Ersatz-Escape-Funktion).

using System;
using System.Text.RegularExpressions;

public static class MyExtensions {
    public static string ReplaceIgnoreCase(this string search, string find, string replace) {
        return Regex.Replace(search ?? "", Regex.Escape(find ?? ""), (replace ?? "").Replace("$", "$$"), RegexOptions.IgnoreCase);          
    }
}

Verwendungszweck:

var result = "This is a test".ReplaceIgnoreCase("IS", "was");
0

Die Methode für reguläre Ausdrücke sollte funktionieren. Sie können jedoch auch die Zeichenfolge aus der Datenbank in Kleinbuchstaben schreiben, die% variables% in Kleinbuchstaben und dann die Positionen und Längen in der unteren Zeichenfolge aus der Datenbank ermitteln. Denken Sie daran, dass sich Positionen in einer Zeichenfolge nicht ändern, nur weil sie niedriger sind.

Dann verwenden Sie eine Schleife, die in eine umgekehrte Richtung geht (es ist einfacher, wenn Sie nicht fortlaufend zählen müssen, wo sich spätere Punkte befinden), entfernen Sie die% variables% nach ihrer Position und aus Ihrer nicht untergeordneten Zeichenfolge Länge und fügen Sie die Ersatzwerte ein.

0
cfeduke

Lass mich meinen Fall machen und dann kannst du mich in Stücke reißen, wenn du willst.

Regex ist nicht die Antwort auf dieses Problem - zu langsam und zu wenig Speicher.

StringBuilder ist viel besser als String Mangling.

Da dies eine Erweiterungsmethode zur Ergänzung von string.Replace sein wird, halte ich es für wichtig, die Funktionsweise anzupassen. Daher ist es wichtig, Ausnahmen für die gleichen Argumente zu werfen. Ebenso wichtig ist die Rückgabe des ursprünglichen Strings, wenn kein Ersatz vorgenommen wurde.

Ich glaube, dass ein StringComparison-Parameter keine gute Idee ist ... Ich habe es ausprobiert, aber der ursprünglich von michael-liu erwähnte Testfall zeigte ein Problem: -

[TestCase("œ", "oe", "", StringComparison.InvariantCultureIgnoreCase, Result = "")]

Während IndexOf übereinstimmt, besteht ein Konflikt zwischen der Länge der Übereinstimmung in der Quellzeichenfolge (1) und oldValue.Length (2). Dies manifestierte sich durch das Auslösen von IndexOutOfRange in einigen anderen Lösungen, wenn oldValue.Length zu der aktuellen Matchposition hinzugefügt wurde, und ich konnte keinen Ausweg aus dieser Richtung finden StringComparison.OrdinalIgnoreCase für meine Lösung verwenden.

Mein Code ist ähnlich wie bei anderen Antworten, aber ich denke, dass ich nach einem Match suche, bevor ich mir die StringBuilder zu schaffen mache. Wenn keine gefunden wird, wird eine potenziell große Zuweisung vermieden. Der Code wird dann zu einem do{...}while und nicht zu einem while{...}.

Ich habe einige ausführliche Tests mit anderen Antworten durchgeführt, und diese waren um einiges schneller und benutzten etwas weniger Speicher.

    public static string ReplaceCaseInsensitive(this string str, string oldValue, string newValue)
    {
        if (str == null) throw new ArgumentNullException(nameof(str));
        if (oldValue == null) throw new ArgumentNullException(nameof(oldValue));
        if (oldValue.Length == 0) throw new ArgumentException("String cannot be of zero length.", nameof(oldValue));

        var position = str.IndexOf(oldValue, 0, StringComparison.OrdinalIgnoreCase);
        if (position == -1) return str;

        var sb = new StringBuilder(str.Length);

        var lastPosition = 0;

        do
        {
            sb.Append(str, lastPosition, position - lastPosition);

            sb.Append(newValue);

        } while ((position = str.IndexOf(oldValue, lastPosition = position + oldValue.Length, StringComparison.OrdinalIgnoreCase)) != -1);

        sb.Append(str, lastPosition, str.Length - lastPosition);

        return sb.ToString();
    }
0
Simon Hewitt