C # - Bir alt dizenin ilk oluşumunu başka bir dizeden kaldırmanın en basit yolu


88

Bir dizenin ilk (ve YALNIZCA ilk) oluşumunu başka bir dizeden kaldırmam gerekiyor.

İşte dizeyi değiştiren bir örnek "\\Iteration". Bu:

ProjeAdı \\ Yineleme \\ Yayın1 \\ Yineleme1

şuna dönüşecekti:

ProjeAdı \\ Yayın1 \\ Yineleme1

İşte bunu yapan bazı kodlar:

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;

Bu çok fazla kod gibi görünüyor.

Öyleyse sorum şu: Bunu yapmanın daha temiz / daha okunaklı / daha özlü bir yolu var mı?

Yanıtlar:


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

10
Bu yanıt, ASCII olmayan karakterleri içeren dizeleri bozabilir. Örneğin, en-US kültürü altında æve aeeşit kabul edilir. Kaldırmaya çalışmadan paediagelen Encyclopædiabir atacağım ArgumentOutOfRangeExceptionsen 6 karakter kaldırmaya çalışıyorsunuz beri Yalnızca alt dize eşleştirme 5. içerdiğinde,
Douglas

6
Bunu şu şekilde değiştirebiliriz: sourceString.IndexOf(removeString, StringComparison.Ordinal)istisnadan kaçınmak için.
Borislav Ivanov

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

DÜZENLEME: @OregonGhost haklı. Ben de böyle bir durumu kontrol etmek için senaryoyu koşullu ifadelerle bölerdim, ancak dizelerin bazı gereklilikler tarafından birbirine ait olduğu varsayımı altında çalışıyordum. İşin gerektirdiği istisna işleme kurallarının bu olasılığı yakalamasının beklenmesi mümkündür. Ben kendim koşullu kontroller yapmak ve ayrıca onu yeterince okumak için zaman ayırmayan genç geliştiriciler için biraz daha okunabilir hale getirmek için birkaç ekstra satır kullanırdım.


9
RemoveString, sourceString'de yer almıyorsa bu kilitlenecektir.
OregonGhost

27
sourceString.Replace(removeString, "");

18
String.Replace , " [r] mevcut örnekteki belirli bir dizenin tüm oluşumlarının belirtilen başka bir dizeyle değiştirildiği yeni bir dize döndürür " diyor. OP, ilk meydana geleni değiştirmek istedi .
Wai Ha Lee

6
Ayrıca, yalnızca kod içeren yanıtlar kabul edilmediğinden yanıtınızı biraz açıklamalısınız. Diğer cevaplara bir göz atın ve bazı ipuçları için bunları sizinkiyle karşılaştırın.
Wai Ha Lee

11

Bunun için hızlı bir TDD Testi yazdı

    [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.Değiştir (giriş, "", 1); "", 1 kez kalıpla eşleşen herhangi bir şey için girişe bakılmasını söylüyor.


2
Problemi çözdüğün gibi. Böyle bir sorun için normal ifadeyi kullanırken performansı göz önünde bulundurun.
Thomas

7

Eğlenmek için bir uzatma yöntemi kullanabilirsiniz . Tipik olarak, string gibi genel amaçlı bir sınıfa genişletme yöntemleri eklemenizi önermiyorum, ancak söylediğim gibi bu eğlenceli. Tekerleği yeniden icat etmenin bir anlamı olmadığı için @ Luke'un cevabını ödünç aldım.

[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
Neden genellikle String gibi genel amaçlı bir sınıfa uzantı yöntemleri eklemeyi önermiyorsunuz? Bunun bariz dezavantajları nelerdir?
Teun Kooijman

1
Böyle genel amaçlı bir sınıfta sahip olamayacak kadar özel bir amaç için bir genişletme yöntemi oluşturmak kolaydır. Örneğin, IsValidIBAN(this string input)dizede olması çok özel olur.
Squirrelkiller

3

Bu sorunu çözmek için basit bir yöntem istiyorsanız. (Bir uzantı olarak kullanılabilir)

Aşağıya bakınız:

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

Kullanım:

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

@ LukeH ve @ Mike'ın cevabından esinlenildi ve değiştirildi.

Culture ayarlarıyla ilgili sorunları önlemek için StringComparison.Ordinal'i unutmayın. https://www.jetbrains.com/help/resharper/2018.2/StringIndexOfIsCultureSpecific.1.html


2

Bunun bir uzatma yöntemi için mükemmel olduğuna kesinlikle katılıyorum, ancak biraz geliştirilebileceğini düşünüyorum.

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

Bu her zaman eğlenceli olan biraz özyineleme yapar.

İşte basit bir birim testi de:

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

    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.