2010-10-21 3 views
30

Я экспортирую матрицу 1200 X 800 (indexMatrix) в файл excel, используя стандартный Microsoft.Office.Interop.Excel. Приложение работает, просто, что это действительно действительно очень медленно (даже для матрицы 100 х 100). Я также экспортирую текстовый файл через TextWriter, и он работает почти мгновенно. Есть ли способ экспортировать файл excel быстрее?Microsoft.Office.Interop.Excel действительно медленный

Вот мой код:

 Excel.Application xlApp=new Excel.Application(); 
     Excel.Workbook xlWorkBook; 
     Excel.Worksheet xlWorkSheet; 
     object misValue = System.Reflection.Missing.Value; 

     //xlApp = new Excel.ApplicationClass(); 
     xlWorkBook = xlApp.Workbooks.Add(misValue); 

     xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1); 
     for (int i = 0; i < 800; i++) //h 
      for (int j = 0; j < 1200; j++) 
       xlWorkSheet.Cells[i+1,j+1] =indexMatrix[i][j]; 


     xlWorkBook.SaveAs("C:\\a.xls", Excel.XlFileFormat.xlWorkbookNormal, misValue, misValue, misValue, misValue, Excel.XlSaveAsAccessMode.xlExclusive, misValue, misValue, misValue, misValue, misValue); 
     xlWorkBook.Close(true, misValue, misValue); 
     xlApp.Quit(); 

     releaseObject(xlWorkSheet); 
     releaseObject(xlWorkBook); 
     releaseObject(xlApp); 

     MessageBox.Show("Excel file created , you can find the file c:\\csharp-Excel.xls"); 

ответ

49

Вы обновляете отдельные ячейки. Это будет очень медленно. Если вы думаете об этом, каждый раз, когда вы обновляете ячейку, вызов RPC будет перенаправлен на процесс Excel.

Это будет более быстрее, если вы присвоите свой двумерный массив значений диапазону Excel одинаковых размеров в одном выражении (один межпроцессный вызов) вместо текущего текущего разрешения 1200 x 800 = 960 000, технологические вызовы.

что-то вроде:

// Get dimensions of the 2-d array 
int rowCount = indexMatrix.GetLength(0); 
int columnCount = indexMatrix.GetLength(1); 
// Get an Excel Range of the same dimensions 
Excel.Range range = (Excel.Range) xlWorkSheet.Cells[1,1]; 
range = range.get_Resize(rowCount, columnCount); 
// Assign the 2-d array to the Excel Range 
range.set_Value(Excel.XlRangeValueDataType.xlRangeValueDefault, indexMatrix); 

На самом деле, чтобы быть педантичным, есть три кросс-процесс вызывает в приведенном выше коде (.Cells, .get_Resize и .set_Value), и есть два вызова на итерации в ваш код (.Cells get и неявный .set_Value) в общей сложности 1200 x 800 x 2 = 1,920,000.

Примечание range.get_Resize и range.set_Value были необходимы для старой версии библиотеки Excel-взаимодействия я использовал, когда этот пост был первым автором. В эти дни вы можете использовать range.Resize и range.Value, как указано в комментарии @ The1nk.

+0

, похоже, проблема в .get_Resize. Кажется, этого не существует. – Alex

+0

на строке excel.range появляется следующая ошибка: «System .__ ComObject» не содержит определения для 'get_Resize' ' – Alex

+0

Я думаю, что редактирование исправит его. Ячейки [1,1] не возвращают объект Range, поэтому его нужно отличать. – Joe

13

Excel Interop никогда не будет быстрым. Вы в основном дистанционно управляете экземпляром приложения Excel. У вас может быть больше успеха, создав файл CSV, а затем с помощью Excel-взаимодействия, чтобы преобразовать его в файл .xls или .xlsx.

+1

+1 Хороший подход, и это также гарантирует быструю работу, поскольку файл CSV, скорее всего, является текстом файл, так что класс 'TextWriter' может обрабатывать его. –

+0

Да, это мой предпочтительный метод. Хотя вы столкнулись с проблемами, когда значения ячеек содержат символы новой строки, так как это закручивает индекс строки. У кого-нибудь есть советы по этой проблеме? – Yevgeniy

+1

В ответ на мой комментарий: используйте import-csv (powershell) или эквивалентную библиотечную функцию, которая будет обрабатывать беспорядочные ситуации с символами классификатора и символами новой строки, используемыми в значениях полей. – Yevgeniy

2

Используйте Value2, чтобы сделать его быстрым; Показывать оригинал перед заполнением данных

6

У меня были схожие проблемы при чтении чрезвычайно большого файла Excel, и потребовалось более 2 часов, используя interop.

Я попытался использовать ClosedXml, и процесс занял менее 10 секунд. ClosedXml

// To loop 
Sheet.Row(y).Cell(x).Value 

Также имейте в виду Interop не будет работать на вашем сервере, если вы не первенствует установлены. ClosedXml не требует установки excel.

+0

Первый раз использовать ClosedXML, и он ДЕЙСТВИТЕЛЬНО быстрый. Я использовал InsertData с массивом массивов и аргументом. Очень плавный по сравнению с HSSF и HSSF сумасшествие POI и COM Исключения interop !!! +1 – Mzn

2

Выключите ScreenUpdating перед тем, как записать данные, Application.ScreenUpdating = FALSE затем включите в конце кода = TRUE

2

ClosedXML это чудо, это намного быстрее и проще в использовании.

var workbook = new XLWorkbook();//create the new book 

var worksheet = workbook.Worksheets.Add("Computer Install");// Add a sheet 
worksheet.Cell(1,1).Value = "PC Name";// (Row, column) write to a cell 

workbook.SaveAs(@"LIC documents.xlsx");// Save the book 

Вы устанавливаете с помощью пакета nu Get. https://www.nuget.org/packages/ClosedXML

+0

Да, это впечатляет. Прошло около 4,5 часов до 5 минут. Github: https://github.com/closedxml/closedxml – Andres

0

Есть три способа сделать это, 2 из которых упоминаются в различных ответах другими:

  1. непосредственно установить значение диапазона в Excel в 2D массива.
  2. Запись данных в файл CSV, а затем использование interop для сохранения CSV-файла в виде файла xls или xlsx.
  3. Запишите данные в файл CSV, затем используйте функцию подключения данных, чтобы использовать CSV в качестве источника данных и импортировать данные.

Все три метода, описанные выше, очень быстрые. Я мог писать данные размером 90000 строк и 100 столбцов примерно за 6 секунд.

P.S. Однако они не решили мою проблему с форматированием данных для границ, стилей шрифтов, цветов, слияния ячеек и т. Д.

Смежные вопросы