字符串.Substring与string.take
本文关键字:take string Substring 字符串 | 更新日期: 2023-09-27 18:03:45
如果您只想取字符串的一部分,则主要使用substring方法。这有一个缺点,必须先测试字符串的长度以避免错误。例如,您希望将数据保存到数据库中,并希望将值截断为前20个字符。
如果执行temp.substring(0,20),但temp只包含10个字符,则抛出异常。
我认为有两种解决方案:
- 测试长度,如果需要,执行子字符串
-
使用扩展方法取
string temp = "1234567890"; var data= new string( temp.Take(20).ToArray()); --> data now holds "1234657890"
当使用Take方法时,在速度或内存使用方面是否有任何缺点?这样做的好处是您不必编写所有这些if语句。
如果您发现自己经常这样做,为什么不编写一个扩展方法呢?
例如: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);
}
}
}
}
正如Henk Holtermand所说,Take()
创建IEnumerator
,然后您需要ToArray()
调用。
因此,如果性能在您的应用程序中很重要,或者您将在进程中多次执行子字符串,则性能可能会出现问题。
我写了一个示例程序来测试Take()
方法到底有多慢,结果如下:
测试了一千万次:
- 执行子串时间:266 ms
- 取操作执行时间:1437 ms
代码如下:
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;
}
首先我不想回答(因为已经有有效的答案),但我想添加一些不适合作为评论的东西:
你说的是性能/内存问题。正确的。正如其他人所说,string.SubString
更高效,因为它是如何内部优化的,因为LINQ是如何与string.Take()
一起工作的(字符枚举…等)。
Take()
在您的例子中的主要缺点是它完全破坏了子字符串的简单性。正如Tim所说,要获得您想要的实际字符串,您必须这样写:
string myString = new string(temp.Take(20).ToArray());
该死的……这比(参见Matthew的扩展方法):
更难理解。string myString = temp.Left(20);
LINQ对于很多用例都很好,但如果没有必要就不应该使用。即使是一个简单的循环有时也比LINQ更好(即更快,更可读/可理解),所以想象一个简单的子字符串…
在你的案例中总结LINQ:
- 表现糟糕
- 少读
- 少可以理解
- 需要LINQ(因此不能与。net 2.0一起使用)
@Daniel的答案的变体,在我看来更准确。
Guid的长度是36。我们正在创建一个字符串长度从1到36不等的列表,我们的目标是使用substring
/take
方法获取18个字符串,因此大约有一半将通过。
我得到的结果表明,Take
将比Substring
慢6-10倍。
结果示例:
Build time: 3812 ms
Time substring: 391 ms, Time take: 1828 ms
Build time: 4172 ms
Time substring: 406 ms, Time take: 2141 ms
因此,对于 500万个字符串,执行大约 250万次操作,总时间为2.1秒,或者大约0.0008564毫秒= ~ 1微秒每次操作。如果你觉得你需要为substring缩短5,那就去做吧,但我怀疑在现实生活中,在tights循环之外,你会感觉到区别。
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;
}
当使用Take方法时,在速度或内存使用方面是否有任何缺点
是的。Take()
包括首先创建IEnumerator<char>
,然后对于每个字符,经过MoveNext()
和yield return;
等的循环。还要注意ToArray和字符串构造函数。
对于少量字符串来说不是问题,但是在大循环中,专用字符串函数要好得多。
Take
扩展方法不创建子字符串,它返回一个可用于创建Char[]
(ToArray)或List<Char>
(ToList)的查询。但你需要的是那个子字符串
那么你还需要其他方法:
string data = new string(temp.Take(20).ToArray());
这隐式地使用foreach
来枚举字符,创建一个新的char[](由于加倍算法可能会分配太多的大小)。最后,从char[]
创建一个新的字符串。
另一方面,Substring
使用优化的方法。
所以你在内存方面付出了一点方便,这可能是可以忽略不计的,但并不总是。