Программная запись нескольких файлов CSV

Я пишу приложение, которое будет записывать таблицы базы данных в файлы CSV. Поскольку многие таблицы содержат более миллиона записей, я даю пользователю возможность записывать большие таблицы в файлы по 25 000 строк в каждом. Я хочу, чтобы пользователь указал исходное имя файла в диалоговом окне SaveFileDialog, а затем добавил «-part1», «-part2» и т. д. для каждого нового записанного файла. Как можно программно записать несколько файлов, пока не будут записаны все данные? Текущий код, который мне нужно написать для файла из 25 000 строк, указан ниже.

    public void ExportPartition(SaveFileDialog saveFile, DataTable table)
    {
        TextWriter writer = new StreamWriter(saveFile.FileName, true, System.Text.Encoding.ASCII, 1048576);

        for (int i = 0; i <= 25000; i++)
        {
            for (int j = 0; j < table.Columns.Count; j++)
            {
                writer.Write(table.Rows[i][j].ToString() + ",");
            }
            writer.Write("\r\n");
        }
        writer.Flush();
        DisposeObjects(saveFile, writer);
    }

person Andrew    schedule 02.08.2011    source источник
comment
Вы также можете рассмотреть возможность обработки полей, в которых уже есть запятые, чтобы операция импорта не завершилась ошибкой.   -  person Bryan Crosby    schedule 02.08.2011
comment
Должна ли быть запятая в конце каждой строки?   -  person alun    schedule 02.08.2011
comment
Я просто написал это быстро, не заметил запятую в конце строки. Спасибо за это. В полях нет данных, содержащих запятые, но для повторного использования я, вероятно, должен решить эту проблему.   -  person Andrew    schedule 02.08.2011


Ответы (2)


    bool ExportPartition(string fileName, DataTable table, int batchSize, int batchNum)
    {
        string fn = string.Format("{0}-{1}{2}",                                     
                                  Path.GetFileNameWithoutExtension(fileName),
                                  batchNum,
                                  Path.GetExtension(fileName));

        fn = Path.Combine(Path.GetDirectoryName(fileName), fn);

        using (TextWriter writer = new StreamWriter(fn))
        {
            int start = batchNum * batchSize;
            int end = start + batchSize;

            for (int i = start; i < end; i++)
            {
                if (i >= table.Rows.Count)
                    break;

                for (int j = 0; j < table.Columns.Count; j++)
                {
                    writer.Write(table.Rows[i][j] + ",");
                }
                writer.Write("\r\n");
            }

            return table.Rows.Count <= end;
        }
    }

Применение:

    void WriteFiles(DataTable table, String fileName, int batchSize)
    {
        int batchNum = 0;          
        bool done = false;
        while (!done)
        {
            done = ExportPartition(fileName, table, batchSize, batchNum++);
        }
    }

    void Main()
    {
        DataTable dt = GetData();
        string fileName = GetFileNameWithSaveDialog(); 
        int batchSize = 25000;
        WriteFiles(dt, fileName, batchSize);
    }
person Anders Forsgren    schedule 02.08.2011
comment
Я реализовал это, однако он вызывает исключение ввода-вывода в конструкторе TextWriter. Это сообщение об ошибке: процесс не может получить доступ к файлу «C:\Users\afannin1\Equipment\HBSSensorDataClient\HBSDataClient\HBSDataClient\bin\Debug\Partitions--0..csv», поскольку он используется другим процессом. - person Andrew; 04.08.2011
comment
Вы видите две точки в имени файла (..csv)? Расширение, вероятно, включает точку, я обновлю строку формата. Используемый файл кажется странным. Вы открывали его в другом приложении (например, в текстовом редакторе или в предыдущем запущенном экземпляре программы)? - person Anders Forsgren; 04.08.2011
comment
Я смог решить эту проблему и проблему с расширением. Приложение сейчас записывает файлы, однако цикл while, используемый в реализации, не завершается. - person Andrew; 04.08.2011
comment
@ Эндрю, я обновил и синтаксис, и вариант использования. Во-первых, он передает имя файла в виде строки, а не всего диалогового окна (которое мы все равно не можем использовать в цикле, поэтому я также заменил его на использование{} для записи). Имя файла теперь также использует имя каталога из имени входного файла, если это полный путь. Я скомпилировал и запустил эти методы на некоторых фиктивных данных. (Полный источник pastebin.com/Y3tXUJgL) - person Anders Forsgren; 04.08.2011

Альтернативное решение:

class Program
{
    static void Main(string[] args)
    {
        DataTable dt = new DataTable();
        dt.Columns.Add("Col1");
        dt.Columns.Add("Col2");
        for (int i = 0; i < 103; ++i)
        {
            var r = dt.NewRow();
            r[0] = Guid.NewGuid().ToString();
            r[1] = i.ToString();
            dt.Rows.Add(r);
        }
        WriteCsvFile(dt, 25, @"C:\temp\test.txt");
    }

    public static string[] ToStringArray(DataRow row)
    {
        var arr = new string[row.Table.Columns.Count];
        for (int j = 0; j < arr.Length; j++)
        {
            arr[j] = row[j].ToString();
            if((arr[j]??"").Contains(","))
                throw new Exception("This will end badly...");
        }
        return arr;
    }

    public static void WriteCsvFile(DataTable table, int maxCount, string fileName)
    {
        if (table.Rows.Count <= maxCount)
            WriteCsvFile(table, maxCount, fileName, 0);
        else
            for (int i = 0; i < (table.Rows.Count / maxCount + 1); ++i)
            {
                var partFileName = Path.Combine(Path.GetDirectoryName(fileName), string.Format("{0}-part{1}{2}", Path.GetFileNameWithoutExtension(fileName), i+1, Path.GetExtension(fileName)));
                WriteCsvFile(table, maxCount, partFileName, i * maxCount);
            }
    }

    public static void WriteCsvFile(DataTable table, int maxCount, string fileName, int startIndex)
    {
        using(var fs = File.Create(fileName))
        using(var w = new StreamWriter(fs, Encoding.ASCII))
        {
            for (int i = startIndex; i < Math.Min(table.Rows.Count, startIndex + maxCount); i++)
                w.WriteLine(String.Join(",", ToStringArray(table.Rows[i])));
            w.Flush();
        }
    }
}
person alun    schedule 02.08.2011