大数据 table 到 .net 中特定大小的多个 csv 文件
Large data table to multiple csv files of specific size in .net
我有一个大数据 table 数百万条记录。我需要将其导出到多个特定大小的 CSV 文件中。因此,例如,我选择 5MB 的文件大小,当我说导出时,数据 table 将导出到 4 个 CSV 文件,每个文件大小为 5MB,最后一个文件大小可能因剩余记录而异。我在这里经历了很多解决方案,也查看了 csvhelper 库,但所有处理大文件的交易都会根据指定的文件大小拆分为多个 CSV,但不会将内存数据 table 拆分为多个 CSV 文件。我想在 C# 中执行此操作。在这方面的任何帮助都会很棒。
谢谢
周杰伦
感谢@H.G.Sandhagen 和@jdweng 的投入。目前我已经编写了以下代码来完成所需的工作。我知道它并不完美,如果我们可以 pre-determine 超出数据 table 项目数组的长度,那么肯定可以进行一些改进并且可以提高效率,如 Nick.McDermaid 所指出的。截至目前,我将使用此代码解除对自己的封锁,并将 post 编码后的最终优化版本。
public void WriteToCsv(DataTable table, string path, int size)
{
int fileNumber = 0;
StreamWriter sw = new StreamWriter(string.Format(path, fileNumber), false);
//headers
for (int i = 0; i < table.Columns.Count; i++)
{
sw.Write(table.Columns[i]);
if (i < table.Columns.Count - 1)
{
sw.Write(",");
}
}
sw.Write(sw.NewLine);
foreach (DataRow row in table.AsEnumerable())
{
sw.WriteLine(string.Join(",", row.ItemArray.Select(x => x.ToString())));
if (sw.BaseStream.Length > size) // Time to create new file!
{
sw.Close();
sw.Dispose();
fileNumber ++;
sw = new StreamWriter(string.Format(path, fileNumber), false);
}
}
sw.Close();
}
我遇到了类似的问题,这就是我用 CsvHelper 解决它的方法。
答案可以很容易地调整为使用 DataTable 作为源。
public void SplitCsvTest()
{
var inventoryRecords = new List<InventoryCsvItem>();
for (int i = 0; i < 100000; i++)
{
inventoryRecords.Add(new InventoryCsvItem { ListPrice = i + 1, Quantity = i + 1 });
}
const decimal MAX_BYTES = 5 * 1024 * 1024; // 5 MB
List<byte[]> parts = new List<byte[]>();
using (var memoryStream = new MemoryStream())
{
using (var streamWriter = new StreamWriter(memoryStream))
using (var csvWriter = new CsvWriter(streamWriter))
{
csvWriter.WriteHeader<InventoryCsvItem>();
csvWriter.NextRecord();
csvWriter.Flush();
streamWriter.Flush();
var headerSize = memoryStream.Length;
foreach (var record in inventoryRecords)
{
csvWriter.WriteRecord(record);
csvWriter.NextRecord();
csvWriter.Flush();
streamWriter.Flush();
if (memoryStream.Length > (MAX_BYTES - headerSize))
{
parts.Add(memoryStream.ToArray());
memoryStream.SetLength(0);
memoryStream.Position = 0;
csvWriter.WriteHeader<InventoryCsvItem>();
csvWriter.NextRecord();
}
}
if (memoryStream.Length > headerSize)
{
parts.Add(memoryStream.ToArray());
}
}
}
for(int i = 0; i < parts.Count; i++)
{
var part = parts[i];
File.WriteAllBytes($"C:/Temp/Part {i + 1} of {parts.Count}.csv", part);
}
}
我有一个大数据 table 数百万条记录。我需要将其导出到多个特定大小的 CSV 文件中。因此,例如,我选择 5MB 的文件大小,当我说导出时,数据 table 将导出到 4 个 CSV 文件,每个文件大小为 5MB,最后一个文件大小可能因剩余记录而异。我在这里经历了很多解决方案,也查看了 csvhelper 库,但所有处理大文件的交易都会根据指定的文件大小拆分为多个 CSV,但不会将内存数据 table 拆分为多个 CSV 文件。我想在 C# 中执行此操作。在这方面的任何帮助都会很棒。
谢谢 周杰伦
感谢@H.G.Sandhagen 和@jdweng 的投入。目前我已经编写了以下代码来完成所需的工作。我知道它并不完美,如果我们可以 pre-determine 超出数据 table 项目数组的长度,那么肯定可以进行一些改进并且可以提高效率,如 Nick.McDermaid 所指出的。截至目前,我将使用此代码解除对自己的封锁,并将 post 编码后的最终优化版本。
public void WriteToCsv(DataTable table, string path, int size)
{
int fileNumber = 0;
StreamWriter sw = new StreamWriter(string.Format(path, fileNumber), false);
//headers
for (int i = 0; i < table.Columns.Count; i++)
{
sw.Write(table.Columns[i]);
if (i < table.Columns.Count - 1)
{
sw.Write(",");
}
}
sw.Write(sw.NewLine);
foreach (DataRow row in table.AsEnumerable())
{
sw.WriteLine(string.Join(",", row.ItemArray.Select(x => x.ToString())));
if (sw.BaseStream.Length > size) // Time to create new file!
{
sw.Close();
sw.Dispose();
fileNumber ++;
sw = new StreamWriter(string.Format(path, fileNumber), false);
}
}
sw.Close();
}
我遇到了类似的问题,这就是我用 CsvHelper 解决它的方法。
答案可以很容易地调整为使用 DataTable 作为源。
public void SplitCsvTest()
{
var inventoryRecords = new List<InventoryCsvItem>();
for (int i = 0; i < 100000; i++)
{
inventoryRecords.Add(new InventoryCsvItem { ListPrice = i + 1, Quantity = i + 1 });
}
const decimal MAX_BYTES = 5 * 1024 * 1024; // 5 MB
List<byte[]> parts = new List<byte[]>();
using (var memoryStream = new MemoryStream())
{
using (var streamWriter = new StreamWriter(memoryStream))
using (var csvWriter = new CsvWriter(streamWriter))
{
csvWriter.WriteHeader<InventoryCsvItem>();
csvWriter.NextRecord();
csvWriter.Flush();
streamWriter.Flush();
var headerSize = memoryStream.Length;
foreach (var record in inventoryRecords)
{
csvWriter.WriteRecord(record);
csvWriter.NextRecord();
csvWriter.Flush();
streamWriter.Flush();
if (memoryStream.Length > (MAX_BYTES - headerSize))
{
parts.Add(memoryStream.ToArray());
memoryStream.SetLength(0);
memoryStream.Position = 0;
csvWriter.WriteHeader<InventoryCsvItem>();
csvWriter.NextRecord();
}
}
if (memoryStream.Length > headerSize)
{
parts.Add(memoryStream.ToArray());
}
}
}
for(int i = 0; i < parts.Count; i++)
{
var part = parts[i];
File.WriteAllBytes($"C:/Temp/Part {i + 1} of {parts.Count}.csv", part);
}
}