C #: il modo più semplice per rimuovere la prima occorrenza di una sottostringa da un'altra stringa


88

Devo rimuovere la prima (e SOLO la prima) occorrenza di una stringa da un'altra stringa.

Ecco un esempio che sostituisce la stringa "\\Iteration". Questo:

ProjectName \\ Iteration \\ Release1 \\ Iteration1

diventerebbe questo:

ProjectName \\ Release1 \\ Iteration1

Ecco del codice che fa questo:

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;

Sembra un sacco di codice.

Quindi la mia domanda è questa: esiste un modo più pulito / più leggibile / più conciso per farlo?

Risposte:


155
int index = sourceString.IndexOf(removeString);
string cleanPath = (index < 0)
    ? sourceString
    : sourceString.Remove(index, removeString.Length);

10
Questa risposta potrebbe non funzionare per le stringhe che coinvolgono caratteri non ASCII. Ad esempio, nella cultura en-US æe aesono considerati uguali. Il tentativo di rimozione paediada Encyclopædiagenererà un ArgumentOutOfRangeException, poiché stai tentando di rimuovere 6 caratteri quando la sottostringa corrispondente ne contiene solo 5.
Douglas

6
Possiamo modificarlo in questo modo: sourceString.IndexOf(removeString, StringComparison.Ordinal)per evitare l'eccezione.
Borislav Ivanov

30
string myString = sourceString.Remove(sourceString.IndexOf(removeString),removeString.Length);

EDIT: @OregonGhost ha ragione. Io stesso spezzerei la sceneggiatura con condizionali per verificare un tale evento, ma operavo partendo dal presupposto che le stringhe fossero date per appartenere l'una all'altra per qualche requisito. È possibile che le regole di gestione delle eccezioni richieste dall'azienda possano cogliere questa possibilità. Io stesso userei un paio di righe extra per eseguire controlli condizionali e anche per renderlo un po 'più leggibile per gli sviluppatori junior che potrebbero non dedicare tempo a leggerlo abbastanza a fondo.


9
Questo andrà in crash se removeString non è contenuto in sourceString.
OregonGhost

27
sourceString.Replace(removeString, "");

18
String.Replace dice che " [r] restituisce una nuova stringa in cui tutte le occorrenze di una stringa specificata nell'istanza corrente vengono sostituite con un'altra stringa specificata ". L'OP voleva sostituire la prima occorrenza.
Wai Ha Lee

6
Inoltre, dovresti spiegare un po 'la tua risposta poiché le risposte di solo codice non sono accettabili. Dai un'occhiata alle altre risposte e confrontale con le tue per alcuni suggerimenti.
Wai Ha Lee

11

Ha scritto un rapido test TDD per questo

    [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 (input, "", 1); dice di cercare nell'input qualcosa che corrisponda al modello, con "", 1 volta.


2
Così hai risolto il problema. Considera solo le prestazioni quando usi le espressioni regolari per un problema come questo.
Thomas

7

Potresti usare un metodo di estensione per divertimento. In genere non consiglio di allegare metodi di estensione a una classe generica come string, ma come ho detto è divertente. Ho preso in prestito la risposta di @ Luke poiché non ha senso reinventare la ruota.

[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);
    }
}

3
Perché in genere non si consiglia di associare metodi di estensione a una classe generica come String? Quali apparenti svantaggi ci sono in questo?
Teun Kooijman

1
È facile costruire un metodo di estensione per uno scopo troppo specifico per averlo in una classe di tale scopo generale. Ad esempio, IsValidIBAN(this string input)sarebbe troppo specifico per averlo su una stringa.
Squirrelkiller

3

Se desideri un metodo semplice per risolvere questo problema. (Può essere utilizzato come estensione)

Vedi sotto:

    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);
    }

Utilizzo:

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

Ispirato e modificato dalle risposte di @ LukeH e @ Mike.

Non dimenticare StringComparison.Ordinal per evitare problemi con le impostazioni delle impostazioni cultura. https://www.jetbrains.com/help/resharper/2018.2/StringIndexOfIsCultureSpecific.1.html


2

Sono assolutamente d'accordo sul fatto che questo sia perfetto per un metodo di estensione, ma penso che possa essere migliorato un po '.

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);
    }

Questo fa un po 'di ricorsione che è sempre divertente.

Ecco anche un semplice 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);

    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.