C # - Cách đơn giản nhất để loại bỏ sự xuất hiện đầu tiên của một chuỗi con khỏi một chuỗi khác


87

Tôi cần xóa lần xuất hiện đầu tiên (và CHỈ là lần đầu tiên) của một chuỗi khỏi một chuỗi khác.

Đây là một ví dụ thay thế chuỗi "\\Iteration". Điều này:

ProjectName \\ Iteration \\ Release1 \\ Iteration1

sẽ trở thành cái này:

ProjectName \\ Release1 \\ Iteration1

Dưới đây là một số mã thực hiện điều này:

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;

Điều đó có vẻ giống như rất nhiều mã.

Vì vậy, câu hỏi của tôi là: Có cách nào rõ ràng hơn / dễ đọc hơn / ngắn gọn hơn để làm điều này không?

Câu trả lời:


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

10
Câu trả lời này có thể bị hỏng đối với các chuỗi liên quan đến các ký tự không phải ASCII. Ví dụ, theo văn hóa en-US, ævà aeđược coi là bình đẳng. Cố gắng xóa paediakhỏi Encyclopædiasẽ ném ra một ArgumentOutOfRangeException, vì bạn đang cố xóa 6 ký tự khi chuỗi con phù hợp chỉ chứa 5.
— Douglas

6
Chúng ta có thể sửa đổi nó như thế này: sourceString.IndexOf(removeString, StringComparison.Ordinal)để tránh ngoại lệ.
— Borislav Ivanov

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

CHỈNH SỬA: @OregonGhost là đúng. Bản thân tôi sẽ chia nhỏ tập lệnh với các điều kiện để kiểm tra sự xuất hiện như vậy, nhưng tôi đang hoạt động với giả định rằng các chuỗi được cho là thuộc về nhau bởi một số yêu cầu. Có thể các quy tắc xử lý ngoại lệ do doanh nghiệp yêu cầu sẽ nắm bắt được khả năng này. Bản thân tôi sẽ sử dụng thêm một vài dòng để thực hiện kiểm tra có điều kiện và cũng để làm cho nó dễ đọc hơn một chút đối với các nhà phát triển cơ sở, những người có thể không dành thời gian để đọc kỹ.


9
Điều này sẽ bị lỗi nếu removeString không có trong sourceString.
— OregonGhost

26
sourceString.Replace(removeString, "");

18
String.Replace nói rằng nó " [r] tạo ra một chuỗi mới trong đó tất cả các lần xuất hiện của một chuỗi được chỉ định trong trường hợp hiện tại được thay thế bằng một chuỗi được chỉ định khác ". OP muốn thay thế lần xuất hiện đầu tiên .
— Wai Ha Lee

6
Ngoài ra, bạn nên giải thích câu trả lời của mình một chút vì câu trả lời chỉ có mã không được chấp nhận. Hãy xem các câu trả lời khác và so sánh chúng với câu trả lời của bạn để biết một số mẹo.
— Wai Ha Lee

11

Đã viết một bài kiểm tra TDD nhanh cho điều này

    [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 (đầu vào, "", 1); cho biết tìm kiếm đầu vào cho bất kỳ thứ gì phù hợp với mẫu, với "", 1 lần.


2
Như vậy là bạn đã giải quyết được vấn đề. Chỉ cần xem xét hiệu suất khi sử dụng regex cho một vấn đề như thế này.
— Thomas

7

Bạn có thể sử dụng một phương pháp mở rộng cho vui. Thông thường, tôi không khuyên bạn nên đính kèm các phương thức mở rộng vào một lớp mục đích chung như chuỗi, nhưng như tôi đã nói, điều này rất thú vị. Tôi mượn câu trả lời của @ Luke vì không có ích gì khi phát minh lại bánh xe.

[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
Tại sao bạn thường không khuyến nghị đính kèm các phương thức mở rộng vào một lớp mục đích chung như String? Điều này có những nhược điểm rõ ràng nào?
— Teun Kooijman,

1
Thật dễ dàng để xây dựng một phương thức mở rộng cho một mục đích quá cụ thể để có nó trên lớp mục đích chung như vậy. Ví dụ, IsValidIBAN(this string input)sẽ quá cụ thể để có nó trên chuỗi.
— Squirrelkiller

3

Nếu bạn muốn một phương pháp đơn giản để giải quyết vấn đề này. (Có thể được sử dụng như một phần mở rộng)

Xem bên dưới:

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

Sử dụng:

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

Lấy cảm hứng từ và sửa đổi câu trả lời của @ LukeH và @ Mike.

Đừng quên StringComparison.Ordinal để ngăn chặn các vấn đề với cài đặt Văn hóa. https://www.jetbrains.com/help/resharper/2018.2/StringIndexOfIsCultureSpecific.1.html


2

Tôi chắc chắn đồng ý rằng điều này là hoàn hảo cho một phương pháp mở rộng, nhưng tôi nghĩ nó có thể được cải thiện một chút.

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

Điều này thực hiện một chút đệ quy luôn thú vị.

Đây cũng là một bài kiểm tra đơn vị đơn giản:

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

    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookie và Chính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.