C# - Самый простой способ удалить первое вхождение подстроки из другой строки


мне нужно удалить первое (и только первое) вхождение строки из другой строки.

вот пример замены строки "Iteration". Это:

ProjectNameIterationRelease1Iteration1

стало бы так:

ProjectNameRelease1Iteration1

вот некоторый код, который делает это:

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;

это похоже на много кода.

Итак, мой вопрос таков: есть ли более чистый/более читаемый/более краткий способ сделать это?

7 61

7 ответов:

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

EDIT: @OregonGhost прав. Я сам разбил бы сценарий с условными обозначениями, чтобы проверить такое событие, но я действовал в предположении, что строки были даны друг другу по какому-то требованию. Возможно, что бизнес-необходимые правила обработки исключений, как ожидается, поймать эту возможность. Я бы сам использовал пару дополнительных строк для выполнения условных проверок, а также для того, чтобы сделать его немного более читаемым для младших разработчиков, которые могут не торопитесь, чтобы прочитать его достаточно тщательно.

написал быстрый тест 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.Заменить(input,"", 1); говорит, чтобы посмотреть на вход для чего-либо, соответствующего шаблону, с"", 1 раз.

sourceString.Replace(removeString, "");

вы могли бы использовать метод расширения для удовольствия. Обычно я не рекомендую прикреплять методы расширения к такому классу общего назначения, как string, но, как я уже сказал, это весело. Я позаимствовал ответ @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);
    }
}

Я определенно согласен, что это идеально подходит для метода расширения, но я думаю, что его можно немного улучшить.

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

    }

Если вы хотите простой способ решить эту проблему. (Может использоваться как расширение)

см. ниже:

    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.

Не забудьте сравнение строк.Порядковый номер для предотвращения проблем с настройками языка и региональных параметров. https://www.jetbrains.com/help/resharper/2018.2/StringIndexOfIsCultureSpecific.1.html