Подтвердить что ты не робот

String.substring vs string.take

Если вы хотите взять только часть строки, в основном используется метод подстроки. Это имеет недостаток, что вы должны сначала проверить длину строки, чтобы избежать ошибок. Например, вы хотите сохранить данные в базе данных и хотите отключить значение до первых 20 символов.

Если вы делаете temp.substring(0,20), но temp содержит только 10 символов, генерируется исключение.

Есть два решения, которые я вижу:

  • проверить длину и выполнить подстроку при необходимости
  • используйте метод расширения Take

        string temp = "1234567890";
        var data= new string( temp.Take(20).ToArray());
        --> data now holds "1234657890"
    

Есть ли недостаток с точки зрения скорости или использования памяти, когда используется метод Take. Преимущество в том, что вам не нужно писать все эти операторы if.

4b9b3361

Ответ 1

Если вы так много делаете, почему бы не написать метод расширения?

Например:

using System;

namespace Demo
{
    public static class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("123456789".Left(5));
            Console.WriteLine("123456789".Left(15));
        }
    }

    public static class StringExt
    {
        public static string Left(this string @this, int count)
        {
            if (@this.Length <= count)
            {
                return @this;
            }
            else
            {
                return @this.Substring(0, count);
            }
        }
    }
}

Ответ 2

Как сказал Хенк Холтерманд, Take() создает IEnumerator, а затем вам нужен вызов ToArray().

Итак, если производительность важна в вашем приложении или вы будете выполнять подстроки несколько раз в своем процессе, производительность может быть проблемой.

Я написал примерную программу для точного определения того, насколько медленнее метод Take(), вот результаты:

Протестировано десять миллионов раз:

  • Время выполнения подстроки: 266 мс
  • Время выполнения: 1437 мс

И вот код:

    internal const int RETRIES = 10000000;

    static void Main(string[] args)
    {
        string testString = Guid.NewGuid().ToString();

        long timeSubstring = MeasureSubstring(testString);
        long timeTake = MeasureTake(testString);

        Console.WriteLine("Time substring: {0} ms, Time take: {1} ms",
            timeSubstring, timeTake);
    }

    private static long MeasureSubstring(string test)
    {
        long ini = Environment.TickCount;

        for (int i = 0; i < RETRIES; i++)
        {
            if (test.Length > 4)
            {
                string tmp = test.Substring(4);
            }
        }

        return Environment.TickCount - ini;
    }

    private static long MeasureTake(string test)
    {
        long ini = Environment.TickCount;

        for (int i = 0; i < RETRIES; i++)
        {
            var data = new string(test.Take(4).ToArray());
        }

        return Environment.TickCount - ini;
    }

Ответ 3

Во-первых, я не хотел отвечать (поскольку уже есть правильные ответы), но я хотел бы добавить что-то, что не подходит в качестве комментария:

Вы говорите о проблемах с производительностью/памятью. Правильно. Как говорили другие, string.SubString является более эффективным из-за того, как он оптимизирован внутри страны и из-за того, как LINQ работает с string.Take() (перечисление символов... и т.д.).

То, что никто не сказал, заключается в том, что основным недостатком Take() в вашем случае является то, что он полностью разрушает простоту подстроки. Как сказал Тим, чтобы получить нужную строку, вы должны будете написать:

string myString = new string(temp.Take(20).ToArray());

Черт... это гораздо труднее понять (см. метод расширения Мэтью):

string myString = temp.Left(20);

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

Подводя итоги LINQ в вашем случае:

  • худшие выступления
  • менее читаемый
  • менее понятный
  • требует LINQ (поэтому не работает с .Net 2.0, например)

Ответ 4

Есть ли недостаток с точки зрения скорости или использования памяти при использовании метода Take

Да. Take() включает в себя сначала создание IEnumerator<char> и для каждого char, проходящего через обручи MoveNext() и yield return; и т.д. Также обратите внимание на ToArray и конструктор строк.

Не проблема для небольшого количества строк, но в большом цикле специализированные строковые функции намного лучше.

Ответ 5

Вариант ответа @Даниэля, который кажется более точным для меня.
Длина Guid составляет 36. Мы создаем список с переменной длиной строк от 1 до 36, и мы будем стремиться к тому, чтобы принимать 18 с помощью методов substring/take, поэтому примерно половина будет проходить.

Результаты, которые я получаю, предполагают, что take будет в 6-10 раз медленнее, чем substring.

Пример результатов:

Build time: 3812 ms
Time substring: 391 ms, Time take: 1828 ms

Build time: 4172 ms
Time substring: 406 ms, Time take: 2141 ms

поэтому для 5 миллионов строк, делая примерно 2,5 миллиона операций, общее время 2,1 секунды или около 0,0008564 миллисекунды = ~ 1 микросекунду за операцию. Если вы чувствуете, что вам нужно вырезать его на 5 для подстроки, пойдите для этого, но я сомневаюсь в реальных ситуациях, за пределами петли колготок, вы когда-нибудь почувствуете разницу.

void Main()
{
    Console.WriteLine("Build time: {0} ms", BuildInput());
    Console.WriteLine("Time substring: {0} ms, Time take: {1} ms", MeasureSubstring(), MeasureTake());
}

internal const int RETRIES = 5000000;
static internal List<string> input;

// Measure substring time
private static long MeasureSubstring()
{
    var v = new List<string>();
    long ini = Environment.TickCount;

    foreach (string test in input)
        if (test.Length > 18)
        {
            v.Add(test.Substring(18));
        }
    //v.Count().Dump("entries with substring");
    //v.Take(5).Dump("entries with Sub");

    return Environment.TickCount - ini;
}

// Measure take time
private static long MeasureTake()
{
    var v = new List<string>();
    long ini = Environment.TickCount;

    foreach (string test in input)
        if (test.Length > 18) v.Add(new string(test.Take(18).ToArray()));

    //v.Count().Dump("entries with Take");
    //v.Take(5).Dump("entries with Take");

    return Environment.TickCount - ini;
}

// Create a list with random strings with random lengths
private static long BuildInput()
{
    long ini = Environment.TickCount;
    Random r = new Random();
    input = new List<string>();

    for (int i = 0; i < RETRIES; i++)
        input.Add(Guid.NewGuid().ToString().Substring(1,r.Next(0,36)));

    return Environment.TickCount - ini;
}

Ответ 6

Метод расширения Take не создает подстроку, он возвращает запрос, который можно использовать для создания Char[] (ToArray) или List<Char> (ToList). Но вы действительно хотите иметь эту подстроку.

Тогда вам понадобятся и другие методы:

string  data = new string(temp.Take(20).ToArray());

Это неявно использует foreach для перечисления символов, создает новый char [] (который может выделять слишком большой размер из-за алгоритма удвоения). Наконец, из Char[] создается новая строка.

С другой стороны, Substring использует оптимизированные методы.

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