将数组拆分为大小有限的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;
}
这里的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();
}