Получить подстроку - все до определенного символа

Я пытаюсь найти лучший способ получить все до символа - в строке. Некоторые примеры строк приведены ниже. Длина строки перед - варьируется и может быть любой длины

223232-1.jpg
443-2.jpg
34443553-5.jpg

поэтому мне нужно значение от начального индекса 0 до прямо перед -. Таким образом, подстроки окажутся равными 223232, 443 и 34443553.


person PositiveGuy    schedule 07.12.2009    source источник


Ответы (9)


Пример .Net Fiddle

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("223232-1.jpg".GetUntilOrEmpty());
        Console.WriteLine("443-2.jpg".GetUntilOrEmpty());
        Console.WriteLine("34443553-5.jpg".GetUntilOrEmpty());

        Console.ReadKey();
    }
}

static class Helper
{
    public static string GetUntilOrEmpty(this string text, string stopAt = "-")
    {
        if (!String.IsNullOrWhiteSpace(text))
        {
            int charLocation = text.IndexOf(stopAt, StringComparison.Ordinal);

            if (charLocation > 0)
            {
                return text.Substring(0, charLocation);
            }
        }

        return String.Empty;
    }
}

Результаты:

223232
443
34443553
344

34
person Fredou    schedule 07.12.2009
comment
Пожалуйста, сделайте им одолжение и добавьте проверку ошибок, если он планирует сделать из этого функцию :) - person Josh; 07.12.2009
comment
Спасибо, я был близок к этому, но мне было любопытно, есть ли у кого-нибудь другой способ сделать это, кроме этой комбинации. Но да, это прекрасно работает и все еще довольно коротко. - person PositiveGuy; 07.12.2009
comment
Если вам нужна однострочник без потери правильной проверки не найдено, вы можете сделать что-то вроде этого: string result = source.Substring(0, Math.Max(source.IndexOf('-'), 0)) - person LukeH; 07.12.2009
comment
Вместо s.Substring(0, n) можно использовать s.Remove(n), когда известно (как здесь), что длина строки s строго превышает n. - person Jeppe Stig Nielsen; 27.11.2014
comment
@LukeH Если IndexOf возвращает -1 в вашем примере, то будет возвращена пустая строка, не так ли? - person crush; 25.06.2015
comment
@crush: Да. Мой пример представляет собой просто однострочный эквивалент ответа Фреду, поэтому он возвращает пустую строку, когда не найден символ -. - person LukeH; 26.06.2015
comment
int l = s.IndexOf(-, StringComparison.Ordinal); - person Kurkula; 08.10.2016
comment
Образец также не работает, если '-' является первым символом строки. Следует использовать ›= вместо ›. - person sjb-sjb; 30.05.2018

Используйте функцию split.

static void Main(string[] args)
{
    string s = "223232-1.jpg";
    Console.WriteLine(s.Split('-')[0]);
    s = "443-2.jpg";
    Console.WriteLine(s.Split('-')[0]);
    s = "34443553-5.jpg";
    Console.WriteLine(s.Split('-')[0]);

Console.ReadKey();
}

Если в вашей строке нет -, вы получите всю строку.

person Dominic Cronin    schedule 12.11.2012
comment
И если у вас есть более одного дефиса, вы получите несколько элементов в своем массиве. - person James Dunne; 12.11.2012
comment
В самом деле, Джеймс, так что это было бы решением только в том случае, если бы вы ожидали только один дефис. Я предполагаю, что вы могли бы использовать методы Linq, такие как skip и агрегат, чтобы получить то, что вы хотели, но тогда у вас будет больше кода, чем уже предложенные методы. Все зависит от того, насколько хорошо вы знаете о поступающих данных. - person Dominic Cronin; 12.11.2012
comment
И несколько лет спустя я только что понял, что был слишком быстр, чтобы уступить точку зрения Джеймса. Вопрос спрашивает, как найти строку перед определенным символом. Таким образом, дальнейшие экземпляры этого символа не имеют значения, и использование [0] просто сработает. Конечно, это еще зависит от того, насколько мы доверяем поступающим данным. А если вообще нет "-"? - person Dominic Cronin; 26.05.2015
comment
Я думаю, что точка зрения @JamesDunne заключалась в том, что, разбивая на массив, вы создаете кучу ненужных строк - ненужный мусор. - person crush; 25.06.2015
comment
Я бы не беспокоился о ненужном мусоре. Любые дополнительные строки, созданные таким образом, будут немедленно недоступны и, следовательно, будут собраны в поколении 0, что действительно является чрезвычайно низкими накладными расходами. Конструкция сборщика мусора явно предназначена для того, чтобы можно было использовать большое количество недолговечных элементов практически без затрат. - person Dominic Cronin; 05.10.2015
comment
+1 Важно отметить для новичков (как и я), что: [0] все разделено влево и [1] все разделено справа от персонажа, от которого отделяется .... по крайней мере, это то, что я сделал, когда заменив 0 на 1 в индексе. - person J.S. Orris; 15.10.2015
comment
Джефф, если у вас больше одного дефиса, вы получите более длинный массив. Перейдите по гиперссылке в моем ответе, чтобы увидеть документы - person Dominic Cronin; 15.10.2015

С тех пор, как началась эта ветка, дела немного продвинулись.

Теперь вы можете использовать

string.Concat(s.TakeWhile((c) => c != '-'));
person Anthony Wieser    schedule 12.12.2014
comment
Как это сравнивает производительность с очевидной комбинацией IndexOf и Substring? Я предполагаю, что он добавляет каждый символ в StringBuilder, а затем создает строку в конце. Там также смешано несколько вызовов функций. Было бы неплохо, если бы Substring мог просто принимать -1 в качестве аргумента длины, означающего конец строки. - person crush; 25.06.2015
comment
Это хуже для эффективности. Как вы говорите, он использует построитель строк, и после проверки кода кажется, что ToString вызывается для каждого символа. Это яснее, если вы ищете более одного символа, так как вы можете легко переписать лямбду. - person Anthony Wieser; 27.06.2015

Один из способов сделать это — использовать String.Substring вместе с String.IndexOf:

int index = str.IndexOf('-');
string sub;
if (index >= 0)
{
    sub = str.Substring(0, index);
}
else
{
    sub = ... // handle strings without the dash
}

Начиная с позиции 0, вернуть весь текст до тире, но не включая его.

person Michael Petrotta    schedule 07.12.2009
comment
если index ‹= 0, вы должны просто вернуть string.empty. - person No Refunds No Returns; 07.12.2009
comment
@NRNR: если ты так говоришь. ОП знает бизнес-требования, а не вы или я. - person Michael Petrotta; 07.12.2009

Слегка изменено и обновлено решение Fredou для C# ≥ 8.

/// <summary>
/// Get substring until first occurrence of given character has been found. Returns the whole string if character has not been found.
/// </summary>
public static string GetUntil(this string that, char @char)
{
    return that[..(IndexOf() == -1 ? that.Length : IndexOf())];
    int IndexOf() => that.IndexOf(@char);
}

Тесты:

[TestCase("", ' ', ExpectedResult = "")]
[TestCase("a", 'a', ExpectedResult = "")]
[TestCase("a", ' ', ExpectedResult = "a")]
[TestCase(" ", ' ', ExpectedResult = "")]
[TestCase("/", '/', ExpectedResult = "")]
[TestCase("223232-1.jpg", '-', ExpectedResult = "223232")]
[TestCase("443-2.jpg", '-', ExpectedResult = "443")]
[TestCase("34443553-5.jpg", '-', ExpectedResult = "34443553")]
public string GetUntil(string input, char until) => input.GetUntil(until);
person Dariusz Woźniak    schedule 23.06.2021

Опираясь на ответ BrainCore:

    int index = 0;   
    str = "223232-1.jpg";

    //Assuming we trust str isn't null 
    if (str.Contains('-') == "true")
    {
      int index = str.IndexOf('-');
    }

    if(index > 0) {
        return str.Substring(0, index);
    }
    else {
       return str;
    }
person n122vu    schedule 03.04.2018

Для этой цели можно использовать регулярные выражения, но лучше избегать дополнительных исключений, когда входная строка не соответствует регулярному выражению.

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

String reStrEnding = Regex.Escape("-");

Я знаю, что это ничего не делает - поскольку "-" совпадает с Regex.Escape("=") == "=", но это будет иметь значение, например, если символ @"\".

Затем нам нужно сопоставить начало строки с окончанием строки или, наоборот, если окончание не найдено, то ничего не сопоставить. (Пустой строки)

Regex re = new Regex("^(.*?)" + reStrEnding);

Если ваше приложение критично к производительности - тогда отдельная строка для нового регулярного выражения, если нет - вы можете иметь все в одной строке.

И, наконец, сопоставьте строку и извлеките соответствующий шаблон:

String matched = re.Match(str).Groups[1].ToString();

И после этого вы можете либо написать отдельную функцию, как это было сделано в другом ответе, либо написать встроенную лямбда-функцию. Я написал сейчас, используя обе нотации - встроенную лямбда-функцию (не допускает параметр по умолчанию) или отдельный вызов функции.

using System;
using System.Text.RegularExpressions;

static class Helper
{
    public static string GetUntilOrEmpty(this string text, string stopAt = "-")
    {
        return new Regex("^(.*?)" + Regex.Escape(stopAt)).Match(text).Groups[1].Value;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Regex re = new Regex("^(.*?)-");
        Func<String, String> untilSlash = (s) => { return re.Match(s).Groups[1].ToString(); };

        Console.WriteLine(untilSlash("223232-1.jpg"));
        Console.WriteLine(untilSlash("443-2.jpg"));
        Console.WriteLine(untilSlash("34443553-5.jpg"));
        Console.WriteLine(untilSlash("noEnding(will result in empty string)"));
        Console.WriteLine(untilSlash(""));
        // Throws exception: Console.WriteLine(untilSlash(null));

        Console.WriteLine("443-2.jpg".GetUntilOrEmpty());
    }
}

Кстати, изменение шаблона регулярного выражения на "^(.*?)(-|$)" позволит подобрать либо до шаблона "-", либо, если шаблон не найден - подобрать все до конца строки.

person TarmoPikaro    schedule 06.01.2019

LINQ-способ

String.Concat("223232-1.jpg".TakeWhile(c => c != '-')) )

(Но вам нужно проверить на нуль;)

person Den    schedule 21.05.2019

person    schedule
comment
голосую за, потому что мне нужно знать индекс разделителя - person Piero Alberto; 29.02.2016
comment
На самом деле это тот же ответ, который дал Фреду (в настоящее время лучший ответ), за исключением того, что он не обрабатывает случай, когда совпадение не найдено. - person Dominic Cronin; 09.05.2016