将数组拆分为大小受限的CSV字符串

时间:2012-02-17 12:18:38

标签: c# .net-3.5 refactoring

我正在寻找一种将大型int []转换为csv字符串的字符串[]的有效方法,其中每个csv限制为最多4000个字符。数组中的值可以是1和int.MaxValue之间的任何值。

这是我的最终代码:

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

有更有效的方法吗?

所以这就是我现在正在使用的(我能够将返回参数更改为List类型以在末尾保存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;
}

表现结果:

10,000,000项csv限制为4000个字符

  • 原文:2,887.488ms
  • GetIntegerDigitCount:3105.355ms
  • 决赛:2883.587ms

虽然我在开发人员计算机上只保存了4ms删除ToArray()调用,但这似乎在速度慢得多的机器上有显着差异(在DELL D620上保存超过200ms)

3 个答案:

答案 0 :(得分:2)

为每个数字创建一个新字符串时,您正在进行大量的堆内存分配,只是为了计算位数。使用following method计算数字中的位数(请参阅下面的方法)。

所以而不是

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

只需使用:

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

<强>结果:

  • 1000万个数字快2倍
  • Old:4316ms,New:1983ms,Diff:2333ms。更快217.6%

编辑:有关大型csv限制的更多结果

  

产品:千万; csvLimit:4000;旧:2091ms,新:1868ms,差异:223ms   更快= 111.937901498929%


代码I用于衡量时间:

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

答案 1 :(得分: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();
    }

答案 2 :(得分: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();
}