XLSX(OOXML)の読み書きは unvell.ReoGrid.IO.Excel が担当します。
外部パッケージに依存しません(Open XML SDK も EPPlus も使いません)。
One パッケージにはすべて同梱されています。
読み書き
using unvell.ReoGrid.Core;
using unvell.ReoGrid.IO.Excel;
Workbook loaded = XlsxReader.Read("book.xlsx");
XlsxWriter.Write(wb, "out.xlsx");
// ストリームからも読める
using var stream = File.OpenRead("book.xlsx");
Workbook fromStream = XlsxReader.Read(stream);
XlsxWriter.Write にもストリーム版があります。
ストリーミング読み込み
読み込みは XmlReader によるストリーミングで、スパースストアへ直接流し込みます。
メモリ使用量は実在するセル数に比例します(シートの宣言上の寸法ではありません)。
巨大ファイルの遅延ロード
// 巨大ファイルはシート単位の遅延ロードで開く
using var virtualBook = XlsxReader.OpenVirtual("large.xlsx", SheetLoadMode.OnDemand);
| モード | 挙動 |
|---|---|
OnDemand | シートを最初に表示したときにそのシートを構築する |
Background | アクティブシートを先に構築し、残りをバックグラウンドで先読みする |
VirtualWorkbook は IDisposable です。元のストリームを保持するため、
使い終わったら破棄してください。
列幅
Excel は列幅を「標準フォントの数字 1 文字分」の倍数で保存し、その数字の幅を表示している画面で 測ります。数字の幅はフォントと DPI で変わり、DPI に比例もしません。Arial 10 は 100% で 7 px ですが、 150% では 10.5 px ではなく 11 px(論理 7.33 px)です。游ゴシック 11 はどの倍率でも 8 px です。
UI ホストで測り方を一度設定すると、開いた xlsx の列が同じ画面の Excel と同じ幅になります。 保存時も同じ測り方で文字数に戻すため、触っていない列幅は読んだときの値のまま書き戻されます。
// 列幅を、同じ画面の Excel と同じ幅で開く(UI ホストで一度だけ設定)
XlsxColumnWidths.MaxDigitWidthMeasurer =
(family, sizePt) => GdiTextMetrics.MaxDigitWidth(family, sizePt, grid.DeviceDpi);
WPF と Avalonia は GDI で描かないので、Excel の数字幅を内蔵の表からその画面の DPI で引く
XlsxColumnWidths.ExcelMaxDigitWidth を渡します。表は Excel と同じ GDI の測り方で、文字の
ピクセルの大きさごとに実測した値なので、150% や 200% でも Excel と同じ幅になります。Avalonia では
DPI に 96 × TopLevel.RenderScaling を使います。
// WPF / Avalonia:Excel の数字幅を、内蔵の表からこの画面の DPI で引く
double dpi = VisualTreeHelper.GetDpi(grid).PixelsPerInchX;
XlsxColumnWidths.MaxDigitWidthMeasurer =
(family, sizePt) => XlsxColumnWidths.ExcelMaxDigitWidth(family, sizePt, dpi);
設定しない場合(サーバーなど)は、100% の Excel が測る数字の幅を同じ表から引きます。 Calibri 11・Aptos Narrow 11・Arial 10 は 7 px、游ゴシック 11・MS Pゴシック 11 は 8 px です。 日本語のブックも 100% の Excel と同じ幅で開きます。表に無いフォントは、Excel の既定の Calibri として 扱います。
数式の扱い
読み込み時に数式は再評価されません。 Excel が保存したキャッシュ値をそのまま採用します。
var wb = XlsxReader.Read("book.xlsx");
// Excel のキャッシュ値をそのまま読むため、ロード時に再評価はしない。
// 自前で計算し直したい場合だけ明示的に呼ぶ。
foreach (var sheet in wb.Worksheets)
sheet.Recalculate();
この設計には理由があります。V5 が実装していない関数を含むファイルでも、Excel が計算した値が
そのまま表示されるためです。再評価すると #NAME? に化けてしまいます。
対応している範囲
| 対象 | 読み | 書き |
|---|---|---|
| 値(数値・文字列・真偽値・日付・エラー) | ○ | ○ |
| 共有文字列・インライン文字列・リッチ文字列 | ○ | ○ |
| スタイル(フォント・色・配置・折り返し) | ○ | ○ |
| 数値書式 | ○ | ○ |
| 罫線 | ○ | ○ |
| 結合 | ○ | ○ |
| 行高・列幅・非表示 | ○ | ○ |
| 1900 / 1904 日付方式 | ○ | ○ |
条件付き書式(<conditionalFormatting> / <dxfs>) | ○ | ○ |
定義名(<definedNames>) | ○ | ○ |
| アウトライン(グループ化) | ○ | ○ |
| 画像(drawing) | ○ | ○ |
ページ設定(<pageSetup>) | ○ | ○ |
ヘッダー・フッター(<headerFooter>)・印刷タイトル | ○ | ○ |
| 表示状態(枠固定・表示倍率・グリッド線)・シートの非表示・タブ色 | ○ | ○ |
オートフィルタ(<autoFilter>) | ○ | ○ |
セルのメモ(comments{n}.xml + VML) | ○ | ○ |
データの入力規則(<dataValidations>) | ○ | ○ |
| 入力規則の x14 拡張(別シート参照のリスト) | ○ | ×(従来要素に書く) |
シート保護(<sheetProtection>) | ○ | ○ |
| 数式 | ○(キャッシュ値を保持) | ○ |
未対応
| 対象 | 備考 |
|---|---|
| チャート・図形 | 画像は対応 |
| セル型 | XLSX に対応する概念がないため通常のセルになる |
| スレッド形式のコメント | 対応するのは従来の「メモ」のみ |
| 交互行スタイル(しま模様) | Excel はテーブルスタイルの一部として持つため |
組込名(_xlnm.*) | Print_Area と Print_Titles のみ PrintSettings として往復。他は未対応 |
| データバーの負の色・ゼロ軸・枠線 | OOXML の x14 拡張が必要 |
| 分割(非固定)ペイン | 枠固定は往復する |
cellStyleXfs(xfId)のスタイル継承 | 読み込み時に展開されない |