2012-02-17 41 views
2

我正在尋找一種將大型int []轉換爲csv字符串的字符串[]的有效方法,其中每個csv被限制爲最多4000個字符。數組中的值可以是1和int.MaxValue之間的任何值。將數組拆分爲大小受限的CSV字符串

這是我的最終代碼:

public static string[] GetCSVsFromArray(int[] array, int csvLimit) 
{ 
    List<string> parts = new List<string>(); 
    StringBuilder sb = new StringBuilder(); 
    foreach(int id in array) 
    { 
     string intId = id.ToString(); 
     if (sb.Length + intId.Length < csvLimit) 
      sb.Append(intId).Append(","); 
     else 
     { 
      if (sb.Length > 0) 
       sb.Length--; 
      parts.Add(sb.ToString()); 
      sb.Length = 0; 
     } 
    } 
    if(sb.Length>0) 
     parts.Add(sb.ToString()); 
    return parts.ToArray(); 
} 

有沒有更有效的方式來做到這一點?

所以這是我現在用(我能夠返回參數更改爲列表類型保存在年底ToArray的()調用):

public static List<string> GetCSVsFromArray(int[] array, int csvLimit) 
{ 
    List<string> parts = new List<string>(); 
    StringBuilder sb = new StringBuilder(); 
    foreach(int id in array) 
    { 
     string intId = id.ToString(); 
     if (sb.Length + intId.Length < csvLimit) 
      sb.Append(intId).Append(","); 
     else 
     { 
      if (sb.Length > 0) 
       sb.Length--; 
      parts.Add(sb.ToString()); 
      sb.Length = 0; 
     } 
    } 
    if(sb.Length>0) 
     parts.Add(sb.ToString()); 
    return parts; 
} 

性能測試結果:

4000個字符

  • 原始

    10,000,000項目CSV限制:2,887.488ms

  • GetIntegerDigitCount:3105.355ms
  • 決賽:2883.587ms

雖然我只保存4ms的去除ToArray的()我的開發機器上調用這個似乎使慢得多的機器(在DELL D620保存200ms以上)上顯著差異

+0

你正在做冗餘'parts.ToArray()'機罩 – sll 2012-02-17 12:38:22

+0

下做了所有項目的內存拷貝你扔掉的價值intId當你創建一個新的行? – Huusom 2012-02-17 12:39:54

+1

我爲什麼會得到[你自己的CSV解析器](http://secretgeek.net/csv_trouble.asp)的印象?請不要這樣做。不要寫這樣的代碼。 – 2012-02-17 12:52:30

回答

2

爲每個數字創建一個新字符串時,您正在執行大量堆內存分配,以計算數位數。使用following method來計算數字中的位數(參見下面的方法)。

所以不是

string intId = id.ToString(); 
if (sb.Length + intId.Length < csvLimit) 

只需使用:

if (sb.Length + this.GetIntegerDigitCount(id) < csvLimit) 

結果:4316ms,新:

  • 2快10萬個號碼
  • 人傑地靈: 1983ms,Diff:2333ms。更快的217.6%

編輯:大CSV限制更多結果

項目:千萬; csvLimit:4000;舊:2091ms,新:1868ms,Diff:223ms 更快= 111。我用來測量時間937901498929%


代碼:

double elapsedOld = 0; 
double elapsedNew = 0; 
int count = 10000000; 
int csvLimit = 4000; 
var items = Enumerable.Range(0, count).ToArray(); 
var watch = Stopwatch.StartNew(); 
this.GetCsVsFromArray(items, csvLimit); 
watch.Stop(); 
elapsedOld = watch.ElapsedMilliseconds; 

watch = Stopwatch.StartNew(); 
this.GetCsVsFromArrayTuned(items, csvLimit); 
watch.Stop(); 
elapsedNew = watch.ElapsedMilliseconds; 
var stat = String.Format(
    "Items:{0}; csvLimit:{1}; Old:{2}ms, New:{3}ms, Diff:{4}ms faster = {5}%",     
    count, 
    csvLimit, 
    elapsedOld, 
    elapsedNew, 
    elapsedOld - elapsedNew, 
    elapsedOld * 100/elapsedNew); 

GetIntegerDigitCount

這裏
public int GetIntegerDigitCount(int valueInt) 
{ 
    double value = valueInt; 
    int sign = 0; 
    if (value < 0) 
    { 
     value = -value; 
     sign = 1; 
    } 

    if (value <= 9) 
    { 
     return sign + 1; 
    } 

    if (value <= 99) 
    { 
     return sign + 2; 
    } 

    if (value <= 999) 
    { 
     return sign + 3; 
    } 

    if (value <= 9999) 
    { 
     return sign + 4; 
    } 

    if (value <= 99999) 
    { 
     return sign + 5; 
    } 

    if (value <= 999999) 
    { 
     return sign + 6; 
    } 

    if (value <= 9999999) 
    { 
     return sign + 7; 
    } 

    if (value <= 99999999) 
    { 
     return sign + 8; 
    } 

    if (value <= 999999999) 
    { 
     return sign + 9; 
    } 

    return sign + 10; 
} 
+0

ToString不僅僅是獲取長度,sb.Append(Int32)在內部做了一個ToString。您的代碼看起來更快,因爲您的字符串限制爲4,這意味着您不會在任何數字> 9999上執行ToString。將csvLimit設置爲4000並檢查結果。 – 2012-02-20 10:00:58

+0

我已經試過csvlimit = 4000,請參閱編輯部分我的答案,基本上統計如下:'Items:10000000; csvLimit:4000;舊的:2091ms,新的:1868ms,Diff:223ms更快= 111.937901498929%' – sll 2012-02-20 12:52:30

+0

真的你是對的,對於大的CSV限制值的差別是相當小的。並感謝關於StringBuilder的內部'ToString()'調用的好處! – sll 2012-02-20 13:23:21

1

的LINQ可以加快事情有點。您的代碼將看起來像這樣一些修改後:

public static string[] GetCSVsFromArray(int[] array, int csvLimit) 
    { 
     List<string> parts = new List<string>(); 
     StringBuilder sb = new StringBuilder(); 
     foreach (string intId in array.Select(id => id.ToString())) 
     { 
      if (sb.Length + intId.Length < csvLimit) 
       sb.Append(intId).Append(","); 
      else 
      { 
       if (sb.Length > 0) 
        sb.Length--; parts.Add(sb.ToString()); sb.Length = 0; 
      } 
     } 
     return parts.ToArray(); 
    } 
+0

您是否每次檢查過這個性能,我總是會比我的原始速度慢一點? – 2012-02-20 10:16:46

+0

我沒有。你有沒有記錄任何與其性能相關的數據? – 2012-02-20 10:24:50

+1

0-10000000,限制4000 - 原始1770ms,你的1843ms; 0-10000000,限制8000 - 原始1808ms,你的1855ms; 10000000-20000000,限制8000 - 原始3971ms,你的4197ms – 2012-02-20 10:37:20

0
using System.Linq;  

public static string[] GetCSVsFromArray(int[] array, int limit) 
{ 
    int i = 0; 
    return array.Select(a => a.ToString()) 
       .GroupBy(a => { i += a.Length; return (i - a.Length)/limit; }) 
       .Select(a => string.Join(",",a)) 
       .ToArray(); 
} 
+0

您試過啓動這段代碼嗎?一些性能指標'項目:1000000.舊:404ms,新:1844ms,差異:-1440ms',非常緩慢。從性能角度來看,縮短代碼片段並不總是更快 – sll 2012-02-17 13:11:45

+0

發佈或調試版本? – k06a 2012-02-17 13:18:31

+0

告訴我輸入數組的大小,請 – k06a 2012-02-17 13:20:11