C # - วิธีที่ง่ายที่สุดในการลบสตริงย่อยที่เกิดขึ้นครั้งแรกจากสตริงอื่น


88

ฉันต้องการลบสตริงแรก (และครั้งแรกเท่านั้น) ที่เกิดขึ้นจากสตริงอื่น

"\\Iteration"นี่คือตัวอย่างแทนที่สตริง นี้:

ProjectName \\ Iteration \\ Release1 \\ Iteration1

จะกลายเป็นสิ่งนี้:

ProjectName \\ Release1 \\ Iteration1

นี่คือรหัสบางส่วนที่ทำสิ่งนี้:

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;

ดูเหมือนว่าจะเป็นรหัสจำนวนมาก

คำถามของฉันคือ: มีวิธีที่สะอาดกว่า / อ่านง่าย / รัดกุมกว่านี้ไหม?

คำตอบ:


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

10
คำตอบนี้อาจแตกสำหรับสตริงที่เกี่ยวข้องกับอักขระที่ไม่ใช่ ASCII ตัวอย่างเช่นภายใต้วัฒนธรรม en-US æและaeถือว่าเท่าเทียมกัน ความพยายามที่จะลบpaediaจากEncyclopædiaจะโยนArgumentOutOfRangeExceptionเนื่องจากคุณกำลังพยายามที่จะเอา 6 ตัวอักษรเมื่อจับคู่ substring มีเพียง 5
ดักลาส

6
เราสามารถแก้ไขได้ดังนี้sourceString.IndexOf(removeString, StringComparison.Ordinal)เพื่อหลีกเลี่ยงข้อยกเว้น
Borislav Ivanov

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

แก้ไข: @OregonGhost ถูกต้อง ตัวฉันเองจะทำลายสคริปต์ด้วยเงื่อนไขเพื่อตรวจสอบเหตุการณ์ดังกล่าว แต่ฉันกำลังดำเนินการภายใต้สมมติฐานที่ว่าสตริงถูกกำหนดให้เป็นของกันและกันโดยข้อกำหนดบางประการ เป็นไปได้ว่ากฎการจัดการข้อยกเว้นที่จำเป็นทางธุรกิจคาดว่าจะจับความเป็นไปได้นี้ ตัวฉันเองจะใช้บรรทัดพิเศษสองสามบรรทัดเพื่อทำการตรวจสอบเงื่อนไขและเพื่อให้อ่านได้ง่ายขึ้นสำหรับนักพัฒนารุ่นเยาว์ที่อาจใช้เวลาในการอ่านอย่างละเอียดไม่เพียงพอ


9
สิ่งนี้จะผิดพลาดหากไม่มี removeString ใน sourceString
OregonGhost

27
sourceString.Replace(removeString, "");

18
String.Replaceระบุว่า " [r] จะเปลี่ยนสตริงใหม่ซึ่งการเกิดขึ้นทั้งหมดของสตริงที่ระบุในอินสแตนซ์ปัจจุบันจะถูกแทนที่ด้วยสตริงที่ระบุอื่น " OP ต้องการแทนที่เหตุการณ์แรก
Wai Ha Lee

6
นอกจากนี้คุณควรอธิบายคำตอบของคุณเล็กน้อยเนื่องจากคำตอบแบบใช้รหัสเท่านั้นไม่สามารถยอมรับได้ ลองดูคำตอบอื่น ๆ และเปรียบเทียบกับคำแนะนำของคุณ
Wai Ha Lee

11

เขียนการทดสอบ TDD ฉบับย่อสำหรับสิ่งนี้

    [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 (อินพุต, "", 1); บอกว่าให้ค้นหาสิ่งที่ตรงกับรูปแบบด้วย "" 1 ครั้ง


2
เช่นเดียวกับที่คุณแก้ไขปัญหา เพียงพิจารณาประสิทธิภาพเมื่อใช้ regex สำหรับปัญหาเช่นนี้
Thomas

7

คุณสามารถใช้วิธีการขยายเพื่อความสนุกสนาน โดยปกติฉันไม่แนะนำให้แนบวิธีการขยายเข้ากับคลาสเอนกประสงค์เช่นสตริง แต่อย่างที่ฉันบอกว่ามันสนุก ฉันยืมคำตอบของ @ Luke เนื่องจากไม่มีประเด็นในการประดิษฐ์วงล้อขึ้นมาใหม่

[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
เหตุใดคุณจึงไม่แนะนำให้แนบวิธีการขยายเข้ากับคลาสวัตถุประสงค์ทั่วไปเช่น String มีข้อเสียอะไรที่ชัดเจนสำหรับสิ่งนี้?
Teun Kooijman

1
เป็นเรื่องง่ายที่จะสร้างวิธีการขยายสำหรับวัตถุประสงค์ที่เฉพาะเจาะจงเกินกว่าที่จะมีในคลาสวัตถุประสงค์ทั่วไปดังกล่าว ตัวอย่างเช่นIsValidIBAN(this string input)จะเจาะจงเกินไปที่จะให้มันอยู่ในสตริง
Squirrelkiller

3

หากคุณต้องการวิธีง่ายๆในการแก้ไขปัญหานี้ (ใช้เป็นส่วนเสริมได้)

ดูด้านล่าง:

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

การใช้งาน:

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

แรงบันดาลใจจากและแก้ไขคำตอบของ @ LukeH และ @ Mike

อย่าลืม StringComparison.Ordinal เพื่อป้องกันปัญหาเกี่ยวกับการตั้งค่าวัฒนธรรม https://www.jetbrains.com/help/resharper/2018.2/StringIndexOfIsCultureSpecific.1.html


2

ฉันยอมรับอย่างแน่นอนว่านี่เหมาะสำหรับวิธีการขยาย แต่ฉันคิดว่ามันสามารถปรับปรุงได้เล็กน้อย

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

นี่เป็นการเรียกซ้ำเล็กน้อยซึ่งสนุกเสมอ

นี่คือการทดสอบหน่วยง่ายๆเช่นกัน:

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

    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.