2013-03-14 13 views
20

如果您只想取一部分字符串,則主要使用子字符串方法。 這有一個缺點,您必須首先測試字符串的長度以避免錯誤。 例如,您想要將數據保存到數據庫中,並且希望將值截斷爲前20個字符。string.substring vs string.take

如果您執行temp.substring(0,20),但temp只能保存10個字符,則會引發異常。

有2個解決方案,我看到:

  1. 測試的長度,並在需要時
  2. 使用擴展方法以做子

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

當使用Take方法時,在速度或內存使用方面是否存在缺陷? 好處是,您不必編寫所有這些if語句。

+1

使用秒錶執行計時。此外,你可以編寫自己的擴展方法來進行檢查。 – 2013-03-14 10:01:23

+0

我會說,採取將枚舉你的字符串。這可能會對長字符串產生巨大影響。 – jbl 2013-03-14 10:03:16

+0

性能是sooooo上下文的。 – crypted 2013-03-14 10:46:08

回答

19

如果你發現自己做了很多,爲什麼不寫一個擴展方法呢?

例如:

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

+1,那就是要走的路 – ken2k 2013-03-14 10:31:07

+0

這確實是我的首選解決方案,它比Take更具可讀性,並且使用子串方法的強大功能。 感謝所有的信息 – Williams 2013-03-14 11:14:42

2

有速度或內存使用方面的任何缺點在於,當一個使用採取方法

是。 Take()涉及首先創建一個IEnumerator<char>,並且對於每個char,經歷MoveNext()yield return;等的環節。還要注意ToArray和字符串構造函數。

對於少量字符串不是問題,但在大循環中,專業字符串函數更好。

+0

yup。收益回報。 linq的可愛性=>收益回報 – 2013-03-14 10:03:44

1

Take擴展方法不會創建子字符串,它會返回一個可用於創建Char[](ToArray)或List<Char>(ToList)的查詢。但是你真的想要這個子字符串。

然後,你需要其他的方法還有:

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

這隱含使用foreach枚舉字符,創建一個新的char [](可能分配由於加倍算法太多的大小)。最後,從char[]創建一個新字符串。另一方面Substring使用optimized methods

所以你付出這個小小的便利與記憶,這可能是微不足道的,但並非總是如此。

12

正如亨克Holtermand說,Take()創建一個IEnumerator,然後你需要的ToArray()電話。

因此,如果性能在您的應用程序中很重要,或者您將在您的過程中多次執行子字符串,則性能可能會成爲問題。

我寫了一個例子程序標杆這裏Take()方法究竟是如何慢是結果:

測試了千萬倍:

  • 時間執行字符串:266毫秒
  • 執行拍攝操作的時間: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; 
    } 
+1

您的代碼不會執行SubString調用,因爲GUID始終長於4個字符。這會使您的測量無效;) – 2013-03-14 10:16:05

+0

是的,您說得對。查看我的編輯。 – 2013-03-14 10:20:32

+1

哇.. 4年後,但嘿...爲什麼不...你一直在測試相同的字符串,獲得相同的結果在他們所有...我已經添加了[答案](https ://stackoverflow.com/a/44916564/1698987),它將創建一個不同長度的輸入字符串列表,然後用更多的熵來執行'substring' /'take'。結果表明「Take」慢6-10倍,但仍然非常快(每次「take」少於0.0008 ms)。 – Noctis 2017-07-05 03:29:45

4

首先,我不想回答(因爲目前已經有有效的答案),但我想補充一點,就是不適合作爲註釋:

你在談論性能/內存問題。對。正如其他人所說,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配合例如)
+0

您可以使用封裝字符串構造的擴展方法: '公共靜態字符串StringJoin(這IEnumerable的字符) { 返回新的字符串(chars.ToArray()); }' ,然後用它如下: '字符串myString的= temp.Take(20).StringJoin();' 如果是可讀性,那麼我認爲這個解決方案也相當考究,否則LINQ是與Substring相比,速度太慢了 – 2017-04-27 13:22:09

2

@Daniel答案的變體對我來說似乎更加準確。
Guid的長度是36.我們創建一個列表,其中的字符串長度從1到36不等,我們的目標是用方法18取得18,所以大約一半的時間會通過。

我得到的結果表明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 

所以,對於5000000串,做大致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; 
} 
相關問題