C# の Excel ライブラリー
from https://creativeweb.jp/aspnet-oss/excel-tools/
C# で Excel ファイルの読み込み及び書き出しの操作が可能な OSS のライブラリーをテストしてみました。テストしたライブラリーは以下の4つです。
1. NPOI
Java の Apacche POI を .NET に移植したものです。OOXML の xlsx 及び 97-2003 の xls ファイルの双方に対応しており、読み込みも書き出しもできる便利なライブラリーです。
問題点としては、xlsx ファイルを作成した場合、Excelでファイルを開くと、「'○○.xlsx'には、読み取れない内容が含まれています。このブックの内容を回復しますか?」というファイルが破損しているエラーが発生します。修復ボタンを押すと問題なく使えますが、あまり気分のいいものではないです。ライセンスは、Apache License 2.0 です。
2. EPPlus
2007/2010形式の xlsx ファイルの読み込み及び書き込みに対応しています。チャートや VBA を操作できる API もあり、Excel に関して言えば NPOI よりも機能は豊富です。処理速度については、ホームページで 50,000セルを1秒で読み込めるとありますが、今回のテストではその程度の性能は問題なくでていました。ライセンスは、LGPL ライセンスです。
3. ClosedXML
Microsoft 純正の Open XML SDK をそのままで使うのはあまりにも面倒なので、それを使いやすくしたライブラリーです。ベースとしてOpen XML SDK を使うので、対応する Excel のファイル形式は、2007/2010 形式になります。Open XML SDK がベースなので機能は豊富ですが、処理は少し重いです。ライセンスは、MIT ライセンスです。
読み込み専用のソフトで、高速に処理できるのが特徴で、海外では人気があるライブラリーです。日本では、97-2003 形式の場合には、日本語がよく文字化けし、2007/2010 の xlsx ファイルの場合は、ふりがなつきで返ってきます。処理が速いだけに残念なのですが、日本語の場合はかなり使いづらいです。ライセンスは、MIT ライセンスです。
テストの方法
Excel ファイルの読み込みと書き出しの簡単なプログラムを書いてテストをしました。テストに使ったプログラムは一番最後においておきます。配列のインデックスが NPOI では 0 からですが、EPPlus と ClosedXML では 1 から始まるのが面白いです。
データのファイルについては、統計データをダウンロードして利用しました。6380行×17列、6160行×17列の2個のファイルで、合計約21万セルで、ファイル容量は xls で合計 3,171KB、xlsx で 合計 1,744KB です。
テスト結果
読み込みだけであれば、Excel Data Reader が抜群に速いです。サーバーで利用する場合には軽いというのは重要な要素ですが、残念なことに日本語の処理に問題があります。現状では、97-2003 形式の日本語の xls ファイルを扱う場合の選択肢は、NPOI しかないようです。2007/2010 形式の xlsx ファイルを扱う場合は、それぞれの特徴を踏まえて利用するライブラリーを選択するようにしたらいいと思います。もう少し処理が速いほうがいいとは思うのですが、それでもサーバーの処理で十分実用になる処理速度だと思います。(単位: ミリ秒)
読込(xls) | 読込(xlsx) | 書込(xls) | 書込(xlsx) | |
NPOI | 925 | 1,887 | 1,289 | 4,000 |
EPPlus | - | 3,084 | - | 4,987 |
ClosedXML | - | 38,174 | - | 45,813 |
Excel Data Reader | 398 | 737 | - | - |
// The DOM approach.
// Note that the code below works only for cells that contain numeric values.
//
static void ReadExcelFileDOM(string fileName)
{
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(fileName, false))
{
WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
SheetData sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();
string text;
foreach (Row r in sheetData.Elements<Row>())
{
foreach (Cell c in r.Elements<Cell>())
{
text = c.CellValue.Text;
Console.Write(text + " ");
}
}
Console.WriteLine();
Console.ReadKey();
}
}
// The SAX approach.
static void ReadExcelFileSAX(string fileName)
{
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(fileName, false))
{
WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
OpenXmlReader reader = OpenXmlReader.Create(worksheetPart);
string text;
while (reader.Read())
{
if (reader.ElementType == typeof(CellValue))
{
text = reader.GetText();
Console.Write(text + " ");
}
}
Console.WriteLine();
Console.ReadKey();
}
}
static DataSet ReadExcelData(string path)
{
DataSet ds = null;
var ext = Path.GetExtension(path);
try
{
using (var stream = File.Open(path, FileMode.Open, FileAccess.Read))
{
using (var reader = ExcelReaderFactory.CreateReader(stream))
{
//ds = reader.AsDataSet();
while (reader.Read())
{
for (int i = 0; i < reader.FieldCount; i++)
{
object value = reader.GetValue(i);
}
}
}
}
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.Message);
}
return ds;
}
private void button1_Click(object sender, EventArgs e)
{
var path = "D:\\cc.xls";
//OpenXmlReader
ReadExcelFileDOM(path);
//ExcelDataReader
var ds = ReadExcelData(path);
foreach (DataTable tbl in ds.Tables)
{
Console.WriteLine("TABLE {0}", tbl.TableName);
if (tbl.TableName.Equals("Statistics"))
{
foreach (DataRow row in tbl.Rows)
{
for (var i = 0; i < tbl.Columns.Count; i++)
{
Console.Write("{0} ", row[i]);
}
Console.WriteLine("");
}
}
}
}
}
留言
張貼留言