为啥在 C# .NET 中写入 Excel 范围所需的时间比预期的要长得多?
Posted
技术标签:
【中文标题】为啥在 C# .NET 中写入 Excel 范围所需的时间比预期的要长得多?【英文标题】:Why does writing to an Excel range take much longer than expected in C# .NET?为什么在 C# .NET 中写入 Excel 范围所需的时间比预期的要长得多? 【发布时间】:2014-11-27 01:02:08 【问题描述】:我在 C# 中构建了一个脚本,该脚本采用 CSV 格式的大型数据文件,并拆分为 Excel 2007+ 格式的两个输出文件。我有满足所有要求的完整工作代码,但在相对较小的源文件上运行我的 save_files()
方法需要大约 15 秒。我想知道是否有更快的方法来做我正在做的事情。
第一个输出最终将多达 180 列(每列 125,000 个点)写入 excel 文件。 (代码的 15 秒运行只使用了 20 列)。 output1_temp_array
是一个 List<string[,]>
,每个列表项都包含一个包含 125k 数据点的字符串数组。 (它被定义为二维数组,因为 Excel 的 Range.Value2
需要一个二维数组,但实际上它是 125k x 1 项宽。)
第二个输出最终以 1 行写入 195 列。填充data_temp_array
的方式(它也是List<string[,]
),我必须将其转置为temp_array
,然后将该temp_array 写入Excel 范围。
以下是部分代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;
using MyExcel = Microsoft.Office.Interop.Excel;
namespace TransposeAThing
public partial class Form1 : Form
private void save_files()
MyExcel.Application excelApp = null;
MyExcel.Workbook excelWorkbook = null;
MyExcel.Worksheet worksheet = null;
MyExcel.Range range = null;
excelApp = new MyExcel.Application();
excelApp.DefaultSaveFormat = XlFileFormat.xlOpenXMLWorkbook;
excelApp.Visible = false;
//Write data into first output file
excelWorkbook = excelApp.Workbooks.Open(xlsOutput1_Filename, 1, false, 5, "", "", false, MyExcel.XlPlatform.xlWindows, "", true, false, 0, true, false, false);
worksheet = excelWorkbook.Worksheets.get_Item("Output Data");
for (int i = 0; i < output1_temp_array.Count(); i++)
range = worksheet.get_Range((MyExcel.Range)worksheet.Cells[1, i + 2], (MyExcel.Range)worksheet.Cells[output1_temp_array[i].Length, i + 2]);
range.set_Value(Type.Missing,output1_temp_array[i]);
excelWorkbook.Save();
excelWorkbook.Close();
//Write data into second output file
string[,] temp_array;
temp_array = new string[1,data_temp_array.Count()];
for (int i = 0; i < data_temp_array.Count(); i++)
temp_array[0,i] = data_temp_array[i][0,0];
excelWorkbook = excelApp.Workbooks.Open(xlsData_Filename, 1, false, 5, "", "", false, MyExcel.XlPlatform.xlWindows, "", true, false, 0, true, false, false);
worksheet = excelWorkbook.Worksheets.get_Item("Aggregate Data");
int start_row = worksheet.UsedRange.Rows.Count + 1;
range = worksheet.get_Range((MyExcel.Range)worksheet.Cells[start_row, 1], (MyExcel.Range)worksheet.Cells[start_row, worksheet.UsedRange.Columns.Count]);
range.Value2 = temp_array;
excelWorkbook.Save();
GC.Collect();
GC.WaitForPendingFinalizers();
if (worksheet != null)
Marshal.FinalReleaseComObject(worksheet);
if (excelWorkbook != null)
excelWorkbook.Close(true, Type.Missing, Type.Missing);
Marshal.FinalReleaseComObject(excelWorkbook);
if (excelApp != null)
excelApp.Quit();
Marshal.FinalReleaseComObject(excelApp);
您知道为什么需要这么长时间才能完成此过程吗?有关如何加快速度的任何提示?
作为参考,我编写的用于执行相同操作的 Python 脚本在相同数据上大约需要 2.3 秒,所以我知道这可能比现在更快。
【问题讨论】:
您是否考虑过使用 OpenXML SDK 而不是 COM?它可能会更快,因为它不必打开 excel。显然,这将意味着重大的重构,但如果您遇到性能问题,则值得考虑。 我同意 Greg 的观点,使用 Openxml 是一种更好的方法。如果它不需要太多重构,你可能想要采用 openxml 方式。 你知道你代码的哪一部分是瓶颈吗?是在构建数据吗?写入文件?一些分析可以帮助您缩小范围。 我将研究 OpenXML。我发布的代码几乎是瓶颈;构建数组的所有代码都需要不到半秒的时间(包括将所有 15MB 以上的数据从 CSV 读取到源数组中)。即使我注释掉 output1 写入,也需要 5 秒。 【参考方案1】:所以在大量深入研究 OpenXML 之后,我采用了一种混合解决方案,它并没有真正解决问题,但至少让它更快。
对于第一个输出文件,我坚持原来的方法。我试了SpreadsheetLight,发现没有writerow
或者range.setValue()
方法,所以写了很多单元格,最后都是嵌套的for
循环。这被证明是非常慢。
private void save_files()
MyExcel.Application excelApp = null;
MyExcel.Workbook excelWorkbook = null;
MyExcel.Worksheet worksheet = null;
MyExcel.Range range = null;
excelApp = new MyExcel.Application();
excelApp.DefaultSaveFormat = XlFileFormat.xlOpenXMLWorkbook;
excelApp.Visible = false;
excelWorkbook = excelApp.Workbooks.Open(xlsOutput1_Filename, 1, false, 5, "", "", false, MyExcel.XlPlatform.xlWindows, "", true, false, 0, true, false, false);
worksheet = excelWorkbook.Worksheets.get_Item("Output Data");
for (int i = 0; i < output1_temp_array.Count(); i++)
range = worksheet.get_Range((MyExcel.Range)worksheet.Cells[1, i + 2], (MyExcel.Range)worksheet.Cells[output1_temp_array[i].Length, i + 2]);
range.set_Value(Type.Missing, output1_temp_array[i]);
excelWorkbook.Save();
excelWorkbook.Close();
然而,对于第二个数据输出,SpreadsheetLight 肯定会缩短时间:
SLDocument data_file = new SLDocument(xlsData_Filename, "Aggregate Data");
SLWorksheetStatistics data_file_info = new SLWorksheetStatistics();
data_file_info = data_file.GetWorksheetStatistics();
int start_row = data_file_info.NumberOfRows + 1;
for (int i = 0; i < data_temp_array.Count(); i++)
data_file.SetCellValue(start_row, i + 1, data_temp_array[i]);
使用此解决方案,现在生成相同的文件需要 8.5 秒而不是 15 秒。
【讨论】:
以上是关于为啥在 C# .NET 中写入 Excel 范围所需的时间比预期的要长得多?的主要内容,如果未能解决你的问题,请参考以下文章
.NET Core(C#) EPPlus写入保存Excel(.xlsx)文件的方法及示例代码
.NET Core(C#) EPPlus创建Excel(.xlsx)写入数据的方法及示例代码