【问题标题】:CSV File Splitting with specific size具有特定大小的 CSV 文件拆分
【发布时间】:2016-04-06 08:19:37
【问题描述】:

大家好,我有一个函数,它将 DataTable 中的 smaller chunks based on size 中的 create multiple CSV files 通过 app.config 键/值对传递。

以下代码的问题:

  1. 我已将文件大小硬编码为 1 kb,当我将传递 20 的值时,它应该创建 20kb 的 csv 文件。目前它正在为相同的值创建5kb 的文件大小。
  2. 对于最后剩下的记录,它没有创建任何文件。

请帮我解决这个问题。谢谢!

代码:

public static void CreateCSVFile(DataTable dt, string CSVFileName)
    {

        int size = Int32.Parse(ConfigurationManager.AppSettings["FileSize"]);
        size *= 1024; //1 KB size
        string CSVPath = ConfigurationManager.AppSettings["CSVPath"];

        StringBuilder FirstLine = new StringBuilder();
        StringBuilder records = new StringBuilder();

        int num = 0;
        int length = 0;

        IEnumerable<string> columnNames = dt.Columns.Cast<DataColumn>().Select(column => column.ColumnName);
        FirstLine.AppendLine(string.Join(",", columnNames));
        records.AppendLine(FirstLine.ToString());

        length += records.ToString().Length;

        foreach (DataRow row in dt.Rows)
        {
            //Putting field values in double quotes
            IEnumerable<string> fields = row.ItemArray.Select(field =>
                string.Concat("\"", field.ToString().Replace("\"", "\"\""), "\""));

            records.AppendLine(string.Join(",", fields));
            length += records.ToString().Length;

            if (length > size)
            {
                //Create a new file
                num++;
                File.WriteAllText(CSVPath + CSVFileName + DateTime.Now.ToString("yyyyMMddHHmmss") + num.ToString("_000") + ".csv", records.ToString());
                records.Clear();
                length = 0;
                records.AppendLine(FirstLine.ToString());
            }

        }            
    }  

【问题讨论】:

  • 我宁愿直接从DataTable 以指定文件大小的较小块直接写入CSV Files。我无法写下所有的记录。 If 条件只查找 length &gt; size,因此它会留下最后留下的记录,而不是为这些记录创建任何文件。

标签: c# file csv header tableheader


【解决方案1】:

使用File.ReadLinesLinq 表示将执行deferred execution

foreach(var line in File.ReadLines(FilePath))
{
   // logic here.
}

来自 MSDN

ReadLinesReadAllLines 方法的区别如下: ReadLines,可以开始枚举之前的字符串集合 整个集合被退回;当您使用 ReadAllLines 时,您必须 等待返回整个字符串数组,然后才能访问 数组。因此,当您处理非常大的文件时, ReadLines 可以更高效

现在,你可以重写你的方法如下。

    public static void SplitCSV(string FilePath, string FileName)
    {
        //Read Specified file size
        int size = Int32.Parse(ConfigurationManager.AppSettings["FileSize"]);

        size *= 1024 * 1024;  //1 MB size

        int total = 0;
        int num = 0;
        string FirstLine = null;   // header to new file                  
        var writer = new StreamWriter(GetFileName(FileName, num));

        // Loop through all source lines
        foreach (var line in File.ReadLines(FilePath))
        {
            if (string.IsNullOrEmpty(FirstLine)) FirstLine = line;
            // Length of current line
            int length = line.Length;

            // See if adding this line would exceed the size threshold
            if (total + length >= size)
            {
                // Create a new file
                num++;
                total = 0;
                writer.Dispose();
                writer = new StreamWriter(GetFileName(FileName, num));
                writer.WriteLine(FirstLine);
                length += FirstLine.Length;
            }

            // Write the line to the current file                
            writer.WriteLine(line);

            // Add length of line in bytes to running size
            total += length;

            // Add size of newlines
            total += Environment.NewLine.Length;
        }
   }

【讨论】:

  • 正如您在我发布的问题中看到的那样,我还在每个文件创建中包含标题 - string FirstLine = arr[0];writer.WriteLine(FirstLine); 如何在 foreach loop?
【解决方案2】:

解决方案非常简单...您不需要将所有行都放入内存中(就像您在 string[] arr = File.ReadAllLines(FilePath); 中所做的那样)。

相反,在输入文件上创建一个StreamReader,并逐行读取缓冲区。当缓冲区超过您的“阈值大小”时,将其写入磁盘到单个 csv 文件中。代码应该是这样的:

using (var sr = new System.IO.StreamReader(filePath))
{
    var linesBuffer = new List<string>();
    while (sr.Peek() >= 0)
    {
        linesBuffer.Add(sr.ReadLine());
        if (linesBuffer.Count > yourThreshold)
        {
            // TODO: implement function WriteLinesToPartialCsv
            WriteLinesToPartialCsv(linesBuffer);
            // Clear the buffer:
            linesBuffer.Clear();
            // Try forcing c# to clear the memory:
            GC.Collect();
        }
    }
}

如您所见,逐行读取流(而不是像您的代码那样读取整个 CSV 输入文件)您可以更好地控制内存。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多