string.substring vs string.take

时间:2013-03-14 09:58:35

标签: c# substring

如果您只想接受字符串的一部分,则主要使用子字符串方法。 这有一个缺点,你必须首先测试字符串的长度,以避免错误。 例如,您希望将数据保存到数据库中,并希望将值剪切为前20个字符。

如果你执行temp.substring(0,20)但temp只保存10个字符,则抛出异常。

我看到有两种解决方案:

  1. 测试长度,并根据需要执行子串
  2. 使用扩展方法拍摄

        string temp = "1234567890";
        var data= new string( temp.Take(20).ToArray());
        --> data now holds "1234657890"
    
  3. 当使用Take方法时,在速度或内存使用方面是否有任何缺点。 好处是您不必编写所有if语句。

6 个答案:

答案 0 :(得分:23)

如果你发现自己这么做了,为什么不写一个扩展方法?

例如:

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);
            }
        }
    }
}

答案 1 :(得分:12)

正如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;
    }

答案 2 :(得分:6)

首先我不想回答(因为已有有效的答案),但我想补充一些不适合作为评论的内容:

你在谈论性能/内存问题。对。正如其他人所说,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)

答案 3 :(得分:3)

@Daniel答案的变体对我来说似乎更准确 一个Guid的长度是36.我们正在创建一个列表,其中可变长度的字符串从1到36,我们的目标是使用html, body { width: 100%; height: 100%; background: white; margin:0; padding:0; } / substring方法取18,所以大约一半将会经历。

我得到的结果表明,take Take慢<6-10倍

结果示例:

Substring

所以,对于 500万字符串,大​​致 250万次操作,总时间 2.1秒,或者 0.0008564毫秒每次操作= ~1微秒。如果你认为你需要将它减去5个子串,那就去吧,但我怀疑在现实生活中,在紧身衣圈之外,你会感受到不同。

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

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

答案 4 :(得分:2)

  

当使用Take方法时,在速度或内存使用方面是否有任何缺点

是。 Take()首先需要创建一个IEnumerator<char>,并且对于每个char,都要经过MoveNext()yield return;等的箍。还要注意ToArray和字符串构造函数。

对于少量字符串不是问题,但在大循环中,专门的字符串函数要好得多。

答案 5 :(得分:1)

Take扩展方法不创建子字符串,它返回一个查询,可用于创建Char[](ToArray)或List<Char>(ToList)。但你实际上想拥有那个子串。

然后你还需要其他方法:

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

这隐含地使用foreach来枚举字符,创建一个新的char [](由于加倍算法可能会分配太多的大小)。最后,从char[]创建一个新字符串。

另一方面Substring使用optimized methods

所以你用记忆支付这个小便利,这可能是微不足道的,但并非总是如此。