string.substring 与 string.take
Posted
技术标签:
【中文标题】string.substring 与 string.take【英文标题】:string.substring vs string.take 【发布时间】:2013-03-02 14:42:55 【问题描述】:如果你想只取字符串的一部分,则多使用 substring 方法。 这有一个缺点,您必须首先测试字符串的长度以避免错误。 例如,您要将数据保存到数据库中,并希望将值截取到前 20 个字符。
如果您执行 temp.substring(0,20) 但 temp 仅包含 10 个字符,则会引发异常。
我看到了 2 个解决方案:
-
测试长度,如果需要,做子串
使用扩展方法采取
string temp = "1234567890";
var data= new string( temp.Take(20).ToArray());
--> data now holds "1234657890"
当使用 Take 方法时,在速度或内存使用方面是否有任何劣势。 好处是您不必编写所有这些 if 语句。
【问题讨论】:
使用秒表进行计时。此外,您可以为执行检查的字符串编写自己的扩展方法。 我会说 Take 会枚举你的字符串。这可能会对长字符串产生巨大的影响。 性能非常符合上下文。temp.SubString(0, Math.Min(20, temp.Length))
【参考方案1】:
@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 微秒。如果你觉得你需要为子字符串减少 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;
【讨论】:
【参考方案2】:首先我不想回答(因为已经有有效的答案),但我想添加一些不适合作为评论的内容:
您在谈论性能/内存问题。正确的。正如其他人所说,string.SubString
效率更高,因为它是如何在内部进行优化的,以及 LINQ 如何与string.Take()
一起工作(字符枚举等)。
没有人说Take()
在您的情况下的主要缺点是它完全破坏了子字符串的简单性。正如蒂姆所说,要获得您想要的实际字符串,您必须编写:
string myString = new string(temp.Take(20).ToArray());
该死...这比(参见 Matthew 的扩展方法)更难理解:
string myString = temp.Left(20);
LINQ 非常适合许多用例,但如果没有必要,则不应使用。即使是一个简单的循环有时也比 LINQ 更好(即更快、更易读/易理解),所以想象一个简单的子字符串......
总结一下你的情况下的LINQ:
表现更差 可读性较差 难以理解 需要 LINQ(例如,不适用于 .Net 2.0)【讨论】:
可以使用一个封装了字符串构造函数的扩展方法:public static string StringJoin(this IEnumerable<char> chars) return new string(chars.ToArray());
,然后使用如下:string myString = temp.Take(20).StringJoin();
如果是可读性,那我觉得这个方案比较优雅,否则LINQ与Substring
相比,速度太慢了【参考方案3】:
正如 Henk Holtermand 所说,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;
【讨论】:
您的代码不会执行 SubString 调用,因为 GUID 总是超过 4 个字符。这会使您的测量无效;) 哇 .. 4 年后,但是嘿... 为什么不... 你一遍又一遍地测试相同的字符串,得到相同的结果...我已经添加了一个answer,它将创建一个不同长度的输入字符串列表,然后使用更多的熵执行substring
/take
。结果表明 Take
慢了 6-10 倍,但仍然非常快(每个 take
不到 0.0008 毫秒)。【参考方案4】:
如果你发现自己经常这样做,为什么不写一个扩展方法呢?
例如:
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);
【讨论】:
这确实是我的首选解决方案,它比 Take 更具可读性,并且使用了 substring 方法的强大功能。谢谢大家的信息【参考方案5】:使用 Take 方法在速度或内存使用方面是否有任何劣势
是的。 Take()
涉及首先创建一个IEnumerator<char>
,并且对于每个字符,都要经过MoveNext()
和yield return;
等的循环。还要注意ToArray 和字符串构造函数。
对于少量字符串来说不是问题,但在大循环中,专门的字符串函数要好得多。
【讨论】:
是的。收益回报。 linq 的可爱 => 收益回报【参考方案6】:Take
扩展方法不创建子字符串,它返回可用于创建 Char[]
(ToArray) 或 List<Char>
(ToList) 的查询。但是您实际上想要拥有该子字符串。
那么你还需要其他方法:
string data = new string(temp.Take(20).ToArray());
这隐含地使用 foreach
来枚举字符,创建一个新的 char[] (由于加倍算法,它可能分配过多的大小)。最后从char[]
创建一个新字符串。
另一方面,Substring
使用 optimized methods。
因此,您为这一点点的便利支付了可能微不足道但并非总是如此的内存。
【讨论】:
以上是关于string.substring 与 string.take的主要内容,如果未能解决你的问题,请参考以下文章
String.subString() 和 String.subSequence() 有啥区别
Java:具有长类型参数的 String.substring()