Разбить массив на строки CSV с ограниченным размером - PullRequest
2 голосов
/ 17 февраля 2012

Я ищу эффективный способ преобразования большого 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();
}

Есть ли более эффективный способ сделать это?

Итак, вот что я сейчас использую (мне удалось изменить параметр возврата на тип списка, чтобы сохранить вызов 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 символов

  • Оригинал: 2887,488мс
  • GetIntegerDigitCount: 3105,355мс
  • Финал: 2883,587мс

Несмотря на то, что я сохранил только 4 мс, удаляя вызов ToArray () на моей машине разработчика, это, кажется, имеет существенное значение на гораздо более медленной машине (сэкономлено более 200 мс на DELL D620)

Ответы [ 3 ]

2 голосов
/ 17 февраля 2012

Вы делаете много выделений памяти кучи при создании новой строки для каждого числа только для вычисления количества цифр.Используйте следующий метод для вычисления количества цифр в номере (см. Метод ниже).

Поэтому вместо

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

Просто используйте:

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

Результаты:

  • В 2 раза быстрее на 10 миллионов чисел
  • Старый: 4316 мс, Новый: 1983 мс, разница: 2333 мс.Быстрее 217,6%

РЕДАКТИРОВАТЬ: Больше результатов по большому пределу CSV

Items: 10000000;csvLimit: 4000;Старый: 2091 мс, Новый: 1868 мс, Разница: 223 мс быстрее = 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;
}
1 голос
/ 17 февраля 2012

Линк здесь может немного ускорить процесс.Ваш код будет выглядеть примерно так после нескольких модификаций:

    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 голосов
/ 17 февраля 2012
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();
}
...