String.substring vs string.take
Если вы хотите взять только часть строки, в основном используется метод подстроки.
Это имеет недостаток, что вы должны сначала проверить длину строки, чтобы избежать ошибок.
Например, вы хотите сохранить данные в базе данных и хотите отключить значение до первых 20 символов.
Если вы делаете temp.substring(0,20), но temp содержит только 10 символов, генерируется исключение.
Есть два решения, которые я вижу:
Есть ли недостаток с точки зрения скорости или использования памяти, когда используется метод Take.
Преимущество в том, что вам не нужно писать все эти операторы if.
Ответы
Ответ 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
использует оптимизированные методы.
Итак, вы платите это небольшое удобство с памятью, которая может быть незначительной, но не всегда.