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

本文关键字:CSV 字符串 数组 拆分 | 更新日期: 2023-09-27 18:25:01

我正在寻找一种有效的方法,将大的int[]转换为csv字符串的string[],其中每个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;
}

性能结果:

10000000项csv限制为4000个字符

  • 原件:2887.488毫秒
  • GetIntegerDigitCount:3105.355ms
  • 决赛:2883.587ms

虽然我在开发人员机器上删除ToArray()调用只节省了4ms,但在速度慢得多的机器上(在DELL D620上节省了200ms)上,这似乎有很大的不同

当为每个数字创建一个新字符串来计算位数时,需要进行大量的堆内存分配。使用以下方法计算数字中的位数(请参阅下面的方法)。

所以不是

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

只需使用:

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

结果:

  • 1000万个数字的速度快2倍
  • 旧:4316ms,新:1983ms,差异:2333ms。更快217.6%

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

项目:10000000;csvLimit:4000;旧:2091ms,新:1868ms,差异: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;
}

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

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