カテゴリー
Software Tech

[C#備忘録]Excelファイルをインポート | AmpiTa Project

 AmpiTa(多用途安否確認システム)に新しいフォームを増設することになり、手順を忘れていることが多いので、次回のために備忘録を作成しています。




ExcelDataReaderの環境づくり

 Excelファイルを扱う際に筆者は2つの方法を用いています。

 今回は読み取るだけなので『ExcelDataReader』を使いました。

 まずは『NuGet』というものへのアクセスからです。画面上のメニューの『ツール』をクリックして展開するメニューから『NuGetパッケージマネジャー』を選択し、さらに展開するサブメニューから『ソリューションのNuGetパッケージの管理』を選びます。


 フォームの編集などを行う画面のタブに『NuGet – ソリューション』というタブが追加されます。

 そこにある検索窓に『ExcelDataReader』と入力します。

 下図の例では『インストール済』のタブを選択しているのでリストされません。


 タブを『参照』に切り替えると、リストに『ExcelDataReader』と『ExcelDataReader.DataSet』が現れます。


 対象のファイルをクリックすると、右側に下図のような窓が現れますので『インストール』ボタンをクリックします。


 インストールが開始されると下図のようなポップ表示が現れます。あとは流れに沿って進めるだけです。




ExcelDataReaderのコード

 『ExcelDataReader』をフォーム内で使うためには、フォーム内で使いますよという宣言が必要です。

 フォームのコードの一番上で『using ExcelDataReader』と宣言します。

using ExcelDataReader;


 実際に構文内で使う場合には別途『using』の宣言が必要です。

 下記の例では読込むExcelファイルのフルパスを変数『Excel_Load_FilePath』に格納した上で、『File.Open』で対象ファイルを開いて『stream』という名前の所に格納しています。

 開いたファイル(FileStream)を『ExcelDataReader』で読込みます。
 あまりよくわかっていないために備忘録が必要になっている素人ゆえ間違えているかもしれませんが、ここでの処理でExcelファイルはXMLファイルを扱うときと同じような挙動になります。後述しますが『Rows』で行を指定して、その行のカラム番号を指定することで個別のデータが引き出せるようになります。

using (FileStream stream = File.Open(@Excel_Load_FilePath, FileMode.Open, FileAccess.Read))

using (IExcelDataReader exRdr = ExcelReaderFactory.CreateOpenXmlReader(stream))


 実際の構文か『using』の括弧で囲まれた内側で、まずはデータセット(DataSet)を宣言します。
 続いてデータテーブル(DataTable)を宣言します。
 XMLでのデータベース構築と同じです。

【参考】[C#備忘録]XMLファイルで集計

 次にセルからデータを取り出します。
 『ds_Data_Set_Excel.Tables[0].Rows[1][0]』の内容は、データセット『ds_Data_Set_Excel』にあるテーブル番号ゼロを『Tables[0]』で指定しています。この『Tables[0]』は、一般的なエクセルで言うところの1番目のsheetです。

using (IExcelDataReader exRdr = ExcelReaderFactory.CreateOpenXmlReader(stream))
{
   var ds_Data_Set_Excel = exRdr.AsDataSet();
   var dt_Data_Table_Excel = ds_Data_Set_Excel.Tables[0];
   str_Value = Convert.ToString(ds_Data_Set_Excel.Tables[0].Rows[1][0].ToString());
}

 『ds_Data_Set_Excel.Tables[0]』と書かずとも、冒頭の『var』で宣言したとおり、『dt_Data_Table_Excel 』に『ds_Data_Set_Excel.Tables[0]』を格納してあるので、コードは『dt_Data_Table_Excel.Rows[1][0].ToString()』と短くすることができます。

 『Rows[1][0]』はExcelのセルで言うと『A2』セルです。『Rows[2][0]』なら『A3』、『Rows[4][2]』なら『C5』です。
 1つめの括弧がRows、すなわち行を表しています。グラフでいうとY軸にあたるので紛らわしいですが。
 2つめの括弧がColumns、すなわち列を表しています。
 それぞれゼロが原点になるのでExcelのsheet上での『A1』が原点なら『Rows[0][0]』が原点になります。

 『Rows[1][0].ToString()』はExcelのA2セルの内容を文字列(String)として取得せよという内容です。

str_Value = Convert.ToString(dt_Data_Table_Excel.Rows[1][0].ToString());

 文字列(String)として取得していますが、空欄など文字列として処理がうまくいかないことを想定して『Convert.ToString』で予防しています。




もう1つの方法(今回不採用)

 今回は読込むだけなので使いませんでしたが、下記のコードでも読込むことは可能です。

 最初にExcelというアプリケーションを起動します。

 普通にExcelを使っていると『保存しますか?』などのポップ表示で確認を求められることがありますが、ここでは『DisplayAlerts 』を『false』にして表示されないようにしています。仮に未保存の状態でExcelを閉じる指示を出せば、そのまま未保存で終了します。

 行数が多いとExcel画面が表示されているだけでもメモリを消費するので、『Visible』は『false』にしておきます。

 『Excel.Workbook』を『null』として空のブックを、『Excel.Worksheet』を『null』として空のシートを用意します。

 アプリとしてのExcelの用意ができたので、次はファイル個別の操作です。『excelBook』に所定のパスのエクセルファイルを読込みます。これで所定のパスのエクセルファイルをダブルクリックして開いたような状態です。

 『excelSheet 』に『excelBook.ActiveSheet』を格納している段階で、最初に表示されるセルを使うということになっています。別途指示することで変更可能です。今回はシートは1つしかないExcelファイルを使っているので問題ありません。

Excel.Application excelApplication = new Excel.Application();
excelApplication.DisplayAlerts = false;
excelApplication.Visible = false;
Excel.Workbook excelBook = null;
Excel.Worksheet excelSheet = null;
excelBook = excelApplication.Workbooks.Open(Excel_Load_FilePath);
excelSheet = excelBook.ActiveSheet;
str_Value = Convert.ToString(excelSheet.Cells[2, 1].Value)

 セルからのデータ取り出しは下記のようになります。

str_Value = Convert.ToString(excelSheet.Cells[2, 1].Value)

 『ExcelDataReader』の場合は『A2』セルの指定は『Rows[1][0]』でした。Rows, Columnsの数字はゼロスタートでした。

str_Value = Convert.ToString(dt_Data_Table_Excel.Rows[1][0].ToString());

 こちらの方法の場合は『A2』は『[2, 1]』という表現になります。1から始まる点に注意が必要です。また、1つの括弧内に2つの数字をカンマ区切りで入れる点も注意が必要です。

str_Value = Convert.ToString(excelSheet.Cells[2, 1].Value)

 データの取り出しを終えたあとはクローズします。

 何かの間違いがあると、Excelファイルは開いたままであるが、Excelのアプリを起動してもクローズできないという、Windowsを再起動するまでクローズできません。

 そのため、Try~catch~finallyを使って処理しています。正常終了でも異常終了でも、必ずfinallyでクローズする指示を出しています。

 finally
{
   excelBook.Close(false, Type.Missing, Type.Missing);
   Marshal.ReleaseComObject(excelBook);
}




もう1の方法の環境づくり

 Excelアプリをコントロールするためには、COMにライブラリを追加しておく必要があります。これは、標準で用意されていますので採否をチェックボックスで選ぶだけです。

 ソリューションエクスプローラーの『参照』を右クリックして、メニューから『参照』を選びます。


 一覧表から『Microsoft Excel 16.0 Object Library』を選び、OKします。


 コードの冒頭に下記を追記します。

using Excel = Microsoft.Office.Interop.Excel;




スピード

 実装したプログラムを実行してみると、下記のような速度差がみられました。

Excel

 こちらは古典的な方法でCSVファイルを読込んだ場合です。

CSV

 ExcelDataReaderを使ってExcelファイルを読み込むと、200行の名簿を0.183秒で読込んでいます。
 カンマ区切りのCSVファイルを1行ずつ読込んでデータベース化すると、1分4秒かかりました。
 両者とも書込先のデータベースは同じ物を使っています。元となる名簿ファイルは、CSVファイルで制作したものを、Excelファイルにも変換保存しているので、内容は同じです。

Excel
CSV

 200人分でもこれだけの差がでるので、人数が多く成れば歴然とした差が生まれます。
 ExcelDataReaderを使って3,000人分のデータを取得しても1秒未満です。
 先ほどの200人分の15倍のデータ量ですが、所要時間は1.59倍です。0.108秒差なので何倍とかいう感覚はなく、ボタンをクリックしたらすぐに結果が出ているという感じです。




おわりに

 今回はExcelデータをインポートするための備忘録を作成しました。

 処理の簡便さや重さでは『ExcelDataReader』が優位にありますが、昔から使い慣れたオブジェクトライブラリも悪くはないなという感じでした。

 これだけのスピードを得られる処理は、ユーザーに有益ですので、『ExcelDataReader』を提供してくださっている人に感謝です。




関連記事