C#-从另一个字符串中删除第一次出现的子字符串的最简单方法


87

我需要从另一个字符串中删除一个字符串的第一个(并且只有第一个)出现。

这是替换字符串的示例"\\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;

似乎很多代码。

所以我的问题是:是否有一种更清洁/更易读/更简洁的方式来做到这一点?

Answers:


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

10
对于包含非ASCII字符的字符串,此答案可能会中断。例如,在美国文化下,æae被认为是平等的。试图删除paediaEncyclopædia将抛出ArgumentOutOfRangeException,因为你正在试图删除6个字符时,仅匹配串中包含5
道格拉斯

6
我们可以这样修改它:sourceString.IndexOf(removeString, StringComparison.Ordinal)避免出现异常。
Borislav Ivanov

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

编辑:@OregonGhost是正确的。我本人将使用条件将脚本分解以检查是否发生了这种情况,但是我在假设字符串是根据某些要求彼此属于的前提下进行操作的。可能需要业务所需的异常处理规则来捕获这种可能性。我本人将使用一些额外的行来执行条件检查,并使初级开发人员更容易阅读,因为他们可能不花时间来充分阅读它。


9
如果removeString不包含在sourceString中,则会崩溃。
OregonGhost 2010年

26
sourceString.Replace(removeString, "");

18
String.Replace表示它“ [r返回一个新字符串,其中当前实例中所有出现的指定字符串都被另一个指定字符串替换”。OP希望替换第一次出现的情况。
李慧夏

6
另外,您应该对答案作些解释,因为纯代码答案是不可接受的。查看其他答案,并将其与您的答案进行一些比较。
李慧夏

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(input,“”,1); 表示要在输入中查找与模式匹配的任何内容,并使用“”进行1次。


2
这样您就解决了问题。使用正则表达式解决此类问题时,只需考虑性能。
托马斯

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

    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.