2020/05/25

C# (.NET)でMS Officeファイルを扱う - Apache POI & IKVM

C#でWordとかExcelのファイルを読み書きする手段としてはいくつかあるのだけど、無償で利用可能な手段だと、だいたいこの2つが定番っぽい。


Microsoft.Office.Interop

Microsoftが提供している、Visual Studioに付属するライブラリ。
ググればいくらか情報も出てくる。
導入も簡単。参照設定に追加するだけだし。

だけど、超遅い。
10ページほどのdocファイルからテキストを抽出するコードを書いてみたのだけど、
それだけでも5~6秒とかかかる。新しいdocx形式のファイルだといくらかマシなようだが。(2019年製のSSDにCorei5の安価なPCだったけど、それにしたってこれは重すぎる。)

扱うファイルが少数で、それほど実行速度が問題にならないならこれでいい。
さすがにMicrosoftが提供しているだけあって、図形の扱いなど複雑なこともできるのは利点かもしれない。


NPOI

Apache POI というJavaのOfficeファイルを読み書きするライブラリを.NETに移植したもの。
NuGetから導入できる。高速に動作する。

ただし、扱えるファイルの種類は xls, xlsx, docx の3種類のみ。
元のApache POIだと doc, ppt, pptx などOffice系のファイルは一通り対応しているのだけど、どういう理由か扱えるのは前述の3種のみ。
また、テキストボックスや図形などには対応できていないようだ。


で、問題は、

  • それなりの速度が欲しい
  • doc, ppt, pptx なども扱いたい
  • テキストボックスや図形なども扱いたい
という場合。

Java の Apache POI を .NET から呼び出せるようにすればいいんじゃね?
と思うのだが、それが難しいからこそ NPOI を作ってるんじゃないのかなー
とか思いつつ調べていたら、ずばりそういうのがあるようだ。



wikipedia
「Mono及び.NET Framework上で実装された、Java仮想マシンを含むJavaの実装」
「他実装と比べ2重の仮想化が行われるため速度面では不利ではあるが、.NET上からJavaの各種ライブラリ等をプログラミング言語レベルで一切意識せずに利用できる。」
だそうな。(wikipediaより)

2015年あたりで更新が終了してしまっているのが惜しいところか。
でもソースもバイナリも公開は継続しているので、利用可能。

使い方よくわかんないし、
だれかこれで Apache POI を .NET で使えるようにしたモノを公開してないかなー
とか日和ったことを考えながら調べていると、
・・・あったよ。NuGetにあるじゃん。



「poi」で検索するといくつかそれっぽいのがでてくるのだけど、
試してみたところ、作成者「qcat」さんの「poi」が使えるようだ。
(「poi-ikvm」という名前のもあるが、こちらは対応するファイル形式が限られていて、なんか半端なもの(失礼)のようだ。)

これをインストールすると、プロジェクトの参照設定に
「poi」に加えて「IKVM.~」というライブラリが大量に追加される。


Apache POI でググると情報はたくさん出てくるのに加えて、
JavaとC#は構文がかなり似ているので、それほど参考資料には困らないはずなので、
ここではPOIの使い方については省略。

ただ、実際に試してみると、docxファイルを開いたときにエラーが発生したので、その対処法について。
エラーの内容を見ると、なにかXML関連のライブラリの参照が足りてない様子。で、↑の画像の参照設定と、IKVMの公式サイトからダウンロードしてきたバイナリ一式(ver. 7.2.4630.5)を見比べてみると、XMLと名前の付くもので
  • IKVM.OpenJDK.XML.Crypto.dll
  • IKVM.OpenJDK.XML.Transform.dll
  • IKVM.OpenJDK.XML.WebServices.dll
  • IKVM.OpenJDK.XML.XPath.dll
の4つが足りてないようなので、これら4つのファイルを直接、参照設定に追加すると、無事にエラーが解消した。
(Visual Studio のソリューションエクスプローラーで「参照」を右クリック
→ 参照マネージャー左側の「参照」 を選択
→ 右下の「参照」ボタン(OKボタンのとなり)で、上記4つのdllを選択)

試してみると、wikipediaでは速度面では不利なんて書いてあるけれども、十分実用範囲内だと思われる。
10ページ程度のdocxファイルからテキストをすべて抽出するのに約200msほど、同じファイルをdoc形式で保存したファイルだと50msほど(なぜか旧形式のほうがずっと速い)だった。
もちろんファイルの内容やマシンスペックに依存するだろうが Microsoft.Office.Interop よりは断然速い。

この「qcat」さん製のPOIライブラリは、現在公開されている Apache POI (最新版は4.1 @2020年5月現在)と比べるとバージョンが古いのが難点か。

自前でApache POIをIKVMを通して使えるように、
あとでIKVMの使い方を調べておくべきかな。


※追記 @2020.05.31
POI+IKVMの実行時、内部で大量に例外をスローしているようだ。
例外処理はされているようなので途中でプログラムが落ちたりはしないが、デバッグ出力に大量にエラーメッセージのログが表示される。
このせいで重くなっているらしい。
デバッグ無しで実行すると、かなり軽くなる。
上で.docxファイルだと200ms、.docファイルだと50msほど、と書いているが、これをデバッグ無しで実行するとそれぞれ約50msとか約20msぐらいまで速くなる。