Excel書き込みの実行速度について
C++でExcelを高速に操作したい!との思いから、様々なAPIの実行速度を測定してみました。
今回、利用したい機能は、書き込みなので、書き込み時間しか調査していません。
■40200回セルに書き込みを行った結果
言語 | 手法 | xls | xlsx | 実行時間 |
---|---|---|---|---|
C# | NPOI (xls) | ○ | × | 96.4 ms |
C# | EPPlus (xlsx) | × | ○ | 243.2 ms |
C++ | ExcelCreater (Ver 3.0.3.44.12) | ○ | × | 434.7 ms |
Python | xlwt (読み込みはxlrd) | ○ | × | 458.6 ms |
C# | NPOI (xlsx) | × | ○ | 582.0 ms |
C# | ClosedXML | × | ○ | 667.2ms |
Python | openpyxl | × | ○ | 965.6 ms |
C++ | ExcelCreater (Ver 2.0.8.1) | ○ | × | 8040.5 ms |
C++ | Microsoft Excel 11.0 Object Library | ○ | × | 14171.9 ms |
C# | OpenXML | × | ○ | 2363686 ms |
各APIの実行時間について
.NET系のAPIは、初回実行時に「平均の時間+300ms」ほど時間がかかっていました。
そのため、初回の実行時間を除いた50回の平均時間を算出しています。
.NETアプリの実行テクニック
初回実行時に時間がかかる問題を解決するために、以下の2点についても調査してみようと思います。
- .NET起動時に読み込む
- ネイティブ展開
「C# .NET 速度」←検索キーワード
OpenXMLの実行時間について
目を疑うほど遅かったです。もしかすると、実装方法に問題が有るのかもしれません。
まとめ
・「xls」と「xlsx」の最速を目指す場合
言語 | 手法 | xls | xlsx | 実行時間 |
---|---|---|---|---|
C# | NPOI (xls) | ○ | × | 96.4 ms |
C# | EPPlus (xlsx) | × | ○ | 243.2 ms |
・使用モジュールを1つにする場合
言語 | 手法 | xls | xlsx | 実行時間 |
---|---|---|---|---|
C# | NPOI (xls) | ○ | × | 96.4 ms |
C# | NPOI (xlsx) | × | ○ | 582.0 ms |
今後について
xlsxのみの対応で良かったので、EPPlusを利用する事にしました。
C++から呼び出すことができる、エクセル操作DLLをC#で作成しようと思います。
NPOI
導入方法
- NuGet パッケージの管理の画面から NPOI をインストールします。
※NuGetが最新で無い場合はエラー発生の可能性有り。更新してください。 - 「 using NPOI; 」が読み込めればインストール完了です。
使用方法
PDFにまとまっている資料です。
NPOI の使い方
NPOIのコード例
public static void Check(string Ver)
{
int length = 200;
try
{
string filePath = Directory.GetCurrentDirectory() + "\\NPOI_XML." + Ver;
//ブック作成
var book = CreateNewBook(filePath);
//シート無しのexcelファイルは保存は出来るが、開くとエラーが発生する
book.CreateSheet("Sheet");
//シート名からシート取得
var sheet = book.GetSheet("Sheet");
//セルに設定
WriteCell(sheet, 0, 0, filePath);
//
for (int i = 0; i < length; i++)
{
WriteCell(sheet, 0, i + 1, "【出力確認】");
for (int j = 0; j < length; j++)
{
WriteCell(sheet, j + 1, i + 1, "【確認】i:" + i + " j:" + j);
}
}
//ブックを保存
using (var fs = new FileStream(filePath, FileMode.Create))
{
book.Write(fs);
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
//ブック作成
static IWorkbook CreateNewBook( string filePath )
{
IWorkbook book;
var extension = Path.GetExtension( filePath );
// HSSF => Microsoft Excel(xls形式)(excel 97-2003)
// XSSF => Office Open XML Workbook形式(xlsx形式)(excel 2007以降)
if( extension == ".xls" ) {
book = new HSSFWorkbook();
}
else if( extension == ".xlsx" ) {
book = new XSSFWorkbook();
}
else {
throw new ApplicationException( "CreateNewBook: invalid extension" );
}
return book;
}
//セル設定(文字列用)
public static void WriteCell( ISheet sheet, int columnIndex, int rowIndex, string value )
{
var row = sheet.GetRow( rowIndex ) ?? sheet.CreateRow( rowIndex );
var cell = row.GetCell( columnIndex ) ?? row.CreateCell( columnIndex );
cell.SetCellValue( value );
}
EPPlus
導入方法
- NuGet パッケージの管理の画面から EPPlus をインストールします。
見つからない場合は、NuGetの設定を確認してください。
※NuGetが最新で無い場合はエラー発生の可能性有り。更新してください。 - 「 using OfficeOpenXml; 」が読み込めればインストール完了です。
使用方法
とても分かりやすく解説してくれています。
EPPlusの使い方
NuGet設定方法
「オプション」→「NuGetパッケージマネージャー」→「パッケージソース」に
- nuget.org
https://www.nuget.org/api/v2/
を追加する。
ClosedXML
###導入方法
-
NuGetの更新
ClosedXMLのインストール時のエラー
→サイトにはNuGetのバージョンを落とせと書いてあるけど、ClosedXML自体のバージョンを落としてもインストールできます。 -
NuGet で「ClosedXML」をインストール
Excelの取り扱いにClosedXMLを使用する
ClosedXMLのコード例
class Excel_CHK
{
public void Check()
{
int length = 200;
string path = Directory.GetCurrentDirectory() + "\\Check_ClosedXML.xlsx";
var workbook = new XLWorkbook();
var worksheet = workbook.Worksheets.Add("Sheet");
worksheet.Cell("A1").Value = path;
for (int i = 0; i < length; i++)
{
worksheet.Cell(i + 2, 1).Value = "【出力確認】";
for (int j = 0; j < length; j++)
{
worksheet.Cell(i + 2, j + 2).Value = "【確認】i:" + i + " j:" + j;
}
}
workbook.SaveAs(path);
}
}
openpyxl
導入方法
- 実行したい環境に『pip install OpenPyXL』を行う。
- 「 import openpyxl as px 」が実行できれば正常に導入できています。
コード例
import openpyxl as px
import time
%%time
length = 200
# ファイル名の指定
filename = 'openpyxl_Speed_Check.xlsx'
# ブックの新規作成
wb = px.Workbook()
# シートの選択
ws = wb['Sheet']
# ファイル名の書き込み
ws["A1"].value = filename
# データの書き込み
for i in range(length):
ws.cell(row=i+2, column=1).value = '【出力確認】'
for j in range(length):
ws.cell(row=i+2, column=j+2).value = '【確認】i:{} j:{}'.format(i, j)
# ファイル保存
wb.save(filename)
xlwt
書き込みのみできるPythonライブラリです。
読み込みライブラリは、『xlrd』です。
導入方法
- 実行したい環境に『pip install xlwt』を行う。
- 「 import xlwt 」が実行できれば正常に導入できています。
コード例
import xlwt
%%time
wb = xlwt.Workbook()
sheet = wb.add_sheet('Sheet')
# ファイル名の指定
filename = 'xlwt_Speed_Check.xls'
# ファイル名の書き込み
sheet.write(0, 0, filename)
# データの書き込み
for i in range(length):
sheet.write(i+1, 0, '【出力確認】')
for j in range(length):
Temp = '【確認】i:{} j:{}'.format(i, j)
sheet.write(i+1, j+1,Temp)
# ファイル保存
wb.save(filename)
OpenXML
導入方法
- Open XML SDK 2.0 for Microsoft Officeのインストール
ライブラリのインストーラーを取得し、インストールを行います。
http://www.microsoft.com/ja-jp/download/details.aspx?id=5124 - インストールで生成されたdllファイルをプロジェクトフォルダに移動
インストール先に指定したフォルダ内の「DocumentFormat.OpenXml.dll」をプロジェクトのフォルダにコピーします。 - 「 using DocumentFormat.OpenXml; 」が実行できればインストール完了です。
コード例
public void Check()
{
int length = 200;
string ExcelFilePath = Directory.GetCurrentDirectory() + "\\Check_OpenXML.xlsx";
// Excelファイルを作る
using (SpreadsheetDocument spreadsheetDocument
= SpreadsheetDocument.Create(ExcelFilePath, SpreadsheetDocumentType.Workbook))
{
// ワークブックを用意し、ワークシートを追加する
WorkbookPart workbookpart = spreadsheetDocument.AddWorkbookPart();
workbookpart.Workbook = new Workbook();
WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet(new SheetData());
Sheets sheets
= spreadsheetDocument.WorkbookPart.Workbook.AppendChild<Sheets>(new Sheets());
Sheet sheet = new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart),
SheetId = 1,
Name = "Sheet"
};
sheets.Append(sheet);
// セルA1をワークシートに追加する
writework(1, 1, worksheetPart, ExcelFilePath, CellValues.String);
for (uint i = 0; i < length; i++)
{
writework(1, i + 2, worksheetPart, "【出力確認】", CellValues.String);
for (uint j = 0; j < length; j++)
{
writework(j + 2, i + 2, worksheetPart, "【確認】i:" + i + " j:" + j, CellValues.String);
}
}
// ワークブックを保存する
workbookpart.Workbook.Save();
}
}
private static void writework(uint col, uint row, WorksheetPart WSP, string val, CellValues type)
{
Cell cellt = InsertCellInWorksheet(ToAlphabet(col), row, WSP);
cellt.CellValue = new CellValue(val);
cellt.DataType = new EnumValue<CellValues>(CellValues.String);
}
private static Cell InsertCellInWorksheet(string columnName, uint rowIndex, WorksheetPart worksheetPart)
{
Worksheet worksheet = worksheetPart.Worksheet;
SheetData sheetData = worksheet.GetFirstChild<SheetData>();
string cellReference = columnName + rowIndex.ToString();
// 指定された位置のRowオブジェクト
Row row
= sheetData.Elements<Row>().FirstOrDefault(r => r.RowIndex == rowIndex);
if (row == null)
{
// Rowオブジェクトがまだ存在しないときは作る
row = new Row() { RowIndex = rowIndex };
sheetData.Append(row);
}
// 指定された位置のCellオブジェクト
Cell refCell
= row.Elements<Cell>().FirstOrDefault(c =>
c.CellReference.Value == cellReference);
if (refCell != null)
return refCell; // すでに存在するので、それを返す
// Cellオブジェクトがまだ存在しないときは作って挿入する
Cell nextCell
= row.Elements<Cell>().FirstOrDefault(c =>
string.Compare(c.CellReference.Value, cellReference, true) > 0);
Cell newCell = new Cell() { CellReference = cellReference };
row.InsertBefore(newCell, nextCell);
worksheet.Save();
return newCell;
}
public static string ToAlphabet(uint columnNo)
{
string alphabet = "ZABCDEFGHIJKLMNOPQRSTUVWXY";
string columnStr = string.Empty;
int m = 0;
do
{
m = (int)columnNo % 26;
columnStr = alphabet[m] + columnStr;
columnNo = columnNo / 26;
} while (0 < columnNo && m != 0);
return columnStr;
}