C # - Einfachste Methode zum Entfernen des ersten Auftretens eines Teilstrings aus einem anderen String

84

Ich muss das erste (und NUR das erste) Vorkommen einer Zeichenfolge aus einer anderen Zeichenfolge entfernen.

Hier ist ein Beispiel, das die Zeichenfolge ersetzt "\\Iteration". Dies:

Projektname \\ Iteration \\ Release1 \\ Iteration1

würde dies werden:

Projektname \\ Release1 \\ Iteration1

Hier ein Code, der dies tut:

const string removeString = "\\Iteration";
int index = sourceString.IndexOf(removeString);
int length = removeString.Length;
String startOfString = sourceString.Substring(0, index);
String endOfString = sourceString.Substring(index + length);
String cleanPath = startOfString + endOfString;

Das scheint viel Code zu sein.

Meine Frage lautet also: Gibt es eine sauberere / lesbarere / präzisere Möglichkeit, dies zu tun?

Vaccano
quelle

Antworten:

148
int index = sourceString.IndexOf(removeString);
string cleanPath = (index < 0)
    ? sourceString
    : sourceString.Remove(index, removeString.Length);
LukeH
quelle
10
Diese Antwort kann für Zeichenfolgen mit Nicht-ASCII-Zeichen unterbrochen werden. Zum Beispiel unter der en-US-Kultur æund aegelten als gleich. Der Versuch, paediaaus zu entfernen Encyclopædia, wirft ein ArgumentOutOfRangeException, da Sie versuchen, 6 Zeichen zu entfernen, wenn der passende Teilstring nur 5 enthält.
Douglas
6
Wir können es so ändern: sourceString.IndexOf(removeString, StringComparison.Ordinal)um die Ausnahme zu vermeiden.
Borislav Ivanov
28
string myString = sourceString.Remove(sourceString.IndexOf(removeString),removeString.Length);

EDIT: @OregonGhost ist richtig. Ich selbst würde das Skript mit Bedingungen aufteilen, um auf ein solches Vorkommen zu prüfen, aber ich ging davon aus, dass die Zeichenfolgen aufgrund einer bestimmten Anforderung zueinander gehören sollten. Es ist möglich, dass von Unternehmen geforderte Regeln für die Ausnahmebehandlung diese Möglichkeit nutzen. Ich selbst würde ein paar zusätzliche Zeilen verwenden, um bedingte Überprüfungen durchzuführen und es für Junior-Entwickler, die sich möglicherweise nicht die Zeit nehmen, es gründlich genug zu lesen, ein wenig lesbarer zu machen.

Joel Etherton
quelle
9
Dies stürzt ab, wenn removeString nicht in sourceString enthalten ist.
OregonGhost
26
sourceString.Replace(removeString, "");
Malcolm Waldron
quelle
18
String.Replace gibt an, dass " [r] eine neue Zeichenfolge zurückgibt, in der alle Vorkommen einer angegebenen Zeichenfolge in der aktuellen Instanz durch eine andere angegebene Zeichenfolge ersetzt werden ". Das OP wollte das erste Vorkommen ersetzen .
Wai Ha Lee
6
Außerdem sollten Sie Ihre Antwort ein wenig erläutern, da Nur-Code-Antworten nicht akzeptabel sind. Schauen Sie sich die anderen Antworten an und vergleichen Sie sie mit Ihren, um einige Tipps zu erhalten.
Wai Ha Lee
11

Schrieb dafür einen schnellen TDD-Test

    [TestMethod]
    public void Test()
    {
        var input = @"ProjectName\Iteration\Release1\Iteration1";
        var pattern = @"\\Iteration";

        var rgx = new Regex(pattern);
        var result = rgx.Replace(input, "", 1);

        Assert.IsTrue(result.Equals(@"ProjectName\Release1\Iteration1"));
    }

rgx.Replace (Eingabe, "", 1); sagt, in der Eingabe nach etwas zu suchen, das dem Muster entspricht, mit "", 1 Mal.

CaffGeek
quelle
2
So haben Sie das Problem gelöst. Berücksichtigen Sie bei einem solchen Problem nur die Leistung, wenn Sie Regex verwenden.
Thomas
7

Sie könnten zum Spaß eine Erweiterungsmethode verwenden . Normalerweise empfehle ich nicht, Erweiterungsmethoden an eine solche Allzweckklasse wie string anzuhängen, aber wie gesagt, das macht Spaß. Ich habe mir @ Lukes Antwort geliehen, da es keinen Sinn macht, das Rad neu zu erfinden.

[Test]
public void Should_remove_first_occurrance_of_string() {

    var source = "ProjectName\\Iteration\\Release1\\Iteration1";

    Assert.That(
        source.RemoveFirst("\\Iteration"),
        Is.EqualTo("ProjectName\\Release1\\Iteration1"));
}

public static class StringExtensions {
    public static string RemoveFirst(this string source, string remove) {
        int index = source.IndexOf(remove);
        return (index < 0)
            ? source
            : source.Remove(index, remove.Length);
    }
}
Mike Valenty
quelle
3
Warum empfehlen Sie normalerweise nicht, Erweiterungsmethoden an eine solche Allzweckklasse wie String anzuhängen? Welche offensichtlichen Nachteile hat dies?
Teun Kooijman
1
Es ist einfach, eine Erweiterungsmethode für einen Zweck zu erstellen, der zu spezifisch ist, um sie für eine solche Allzweckklasse zu verwenden. Zum Beispiel IsValidIBAN(this string input)wäre es zu spezifisch, um es in einer Zeichenfolge zu haben.
Eichhörnchenkiller
3

Wenn Sie eine einfache Methode zur Lösung dieses Problems wünschen. (Kann als Erweiterung verwendet werden)

Siehe unten:

    public static string RemoveFirstInstanceOfString(this string value, string removeString)
    {
        int index = value.IndexOf(removeString, StringComparison.Ordinal);
        return index < 0 ? value : value.Remove(index, removeString.Length);
    }

Verwendung:

    string valueWithPipes = "| 1 | 2 | 3";
    string valueWithoutFirstpipe = valueWithPipes.RemoveFirstInstanceOfString("|");
    //Output, valueWithoutFirstpipe = " 1 | 2 | 3";

Inspiriert von und modifiziert die Antwort von @ LukeH und @ Mike.

Vergessen Sie nicht StringComparison.Ordinal, um Probleme mit den Kultureinstellungen zu vermeiden. https://www.jetbrains.com/help/resharper/2018.2/StringIndexOfIsCultureSpecific.1.html

Daniel Filipe
quelle
2

Ich stimme definitiv zu, dass dies perfekt für eine Erweiterungsmethode ist, aber ich denke, dass es ein bisschen verbessert werden kann.

public static string Remove(this string source, string remove,  int firstN)
    {
        if(firstN <= 0 || string.IsNullOrEmpty(source) || string.IsNullOrEmpty(remove))
        {
            return source;
        }
        int index = source.IndexOf(remove);
        return index < 0 ? source : source.Remove(index, remove.Length).Remove(remove, --firstN);
    }

Dies macht ein bisschen Rekursion, was immer Spaß macht.

Hier ist auch ein einfacher Unit-Test:

   [TestMethod()]
    public void RemoveTwiceTest()
    {
        string source = "look up look up look it up";
        string remove = "look";
        int firstN = 2;
        string expected = " up  up look it up";
        string actual;
        actual = source.Remove(remove, firstN);
        Assert.AreEqual(expected, actual);

    }
Greg Roberts
quelle