Open XML SDK で Excel シートを読み込む

Excel 2007 以降では、既定のデータ保存形式が Open XML (Office Open XML, OOXML とも) となりました。
今回は、Open XML 形式で保存された Excel シート (スプレッドシート) のセルの値を取得する方法について記述します。

Open XML 形式の Excel ファイル (.xlsx) は、各データが格納された XML ファイルの集合を ZIP 圧縮したものです。
実際に Excel ファイルの拡張子を .zip に変更してみると、XML ファイルで構成されていることがわかります。

Book1.xlsx.zip

これらの XML ファイル群は、次のようなデータモデルを形成しています (一部のみを記述)。

Spreadsheet 論理データモデル

各ワークシートの ID・名前や順序は workbook.xml に格納されています。
初めにここから ID を取得することにより、ワークシートの実体の場所 (sheet1.xml など) を求めることができます。

また、Excel を利用してデータを保存すると、セルの値が文字列の場合、その実体は sharedStrings.xml に格納されます。
CellValue にはそのインデックスが格納されます。
(数値の場合、CellValue に直接格納されます。)

このような一連の論理データにアクセスするために、Open XML SDK 2.5 が提供されています。
OpenXMLSDKV25.msi には DLL ファイルが含まれており、これをインストールすれば最低限は使えるようになりますが、
Open XML SDK Productivity Tool (OpenXMLSDKToolV25.msi) もインストールしておくとよいでしょう。

Open XML SDK Productivity Tool を使うと、Excel ファイル内の XML を改行・インデント付きで表示できます。
また、現在のデータを作成するための C# のコードも自動生成されます。

Open XML SDK Productivity Tool

 

さて、Open XML SDK で提供されているのは上の図で示したような論理的データへのアクセスであり、概念的な操作ができません。
開発者の観点からすると、例えば、

var valueB3 = sheet["B3"];

または

var valueB3 = sheet[1, 2];

のような方法でセルの値を取得したいところです。

そこで、次のようなラッパー クラスを作成しました。


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;

namespace OpenXmlSample
{
    public class ExcelSheet
    {
        Dictionary<string, string> sheetDataCache;

        public string this[string cellReference]
        {
            get { return sheetDataCache.ContainsKey(cellReference) ? sheetDataCache[cellReference] : null; }
        }

        public void Load(string filePath)
        {
            using (var document = SpreadsheetDocument.Open(filePath, false))
            {
                var sharedStringTable = document.WorkbookPart.SharedStringTablePart.SharedStringTable;
                var sharedStringsCache = sharedStringTable.Elements<SharedStringItem>()
                    .Select((si, i) => new { i, Text = si.Elements<Text>().FirstOrDefault() })
                    .ToDictionary(x => x.i, x => x.Text != null ? x.Text.Text : null);

                Func<Cell, string> getValue = cell =>
                    cell.DataType == null ? cell.CellValue.Text :
                    cell.DataType.Value == CellValues.SharedString ? sharedStringsCache[Convert.ToInt32(cell.CellValue.Text)] :
                    cell.CellValue.Text;

                // ここでは、最初のシートを参照します。
                var sheet = document.WorkbookPart.Workbook.Sheets.Elements<Sheet>().First();
                var worksheetPart = (WorksheetPart)document.WorkbookPart.GetPartById(sheet.Id);
                var sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();
                sheetDataCache = sheetData.Descendants<Cell>()
                    .ToDictionary(c => c.CellReference.Value, getValue);
            }
        }
    }
}


Load メソッドの最初で、SharedStringTable の値をキャッシュしています。

sharedStringTable.ChildElements[12]

のようにインデックスを指定してアクセスすることもできるのですが、
Open XML SDK の内部では辞書的なインデックスが作成されずに都度列挙しているようで、
この方法ではデータの規模が大きくなると取得に時間がかかってしまいます。
そこで、データの列挙を 1 回で済ますために、最初に辞書式のキャッシュを作っておきます。

同様にして、SheetData を辞書式にキャッシュします。
CellReference というのは、"A1" のようなセルの名前 (座標) を表します。

このようなラッパー クラスを作っておくと、次のようにしてセルの値を取得できます。


using System;

namespace OpenXmlSample
{
    static class Program
    {
        static void Main(string[] args)
        {
            var sheet = new ExcelSheet();
            sheet.Load("Book1.xlsx");

            var person = new Person
            {
                Id = sheet["A3"].To<int>(),
                Name = sheet["B3"], 
                Salary = sheet["C3"].To<decimal>(),
            };
        }

        public static T To<T>(this object value)
        {
            return (T)Convert.ChangeType(value, typeof(T));
        }
    }

    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Salary { get; set; }
    }
}


ちなみに、ClosedXML というものが存在するようです。

バージョン情報
Excel 2010
.NET Framework 4

参照
Open XML SDK 2.5
Open XML SDK 2.0 を使用して Excel 2010 ブック内のセルの値を取得する
Open XML を使った Office データの作成
ClosedXML – The easy way to OpenXML

Excel 方眼紙でドット絵を描く
Excel ファイルから画像を自動で取り出す

広告
カテゴリー: .NET Framework. タグ: , . 3 Comments »

Excel 方眼紙でドット絵を描く

Excel方眼紙 Advent Calendar 2013 の 15 日目です。

ドット絵を描くにはどうしたらよいでしょうか。
Web ブラウザー上で使えるツールとしては、例えば X-Icon Editor などがあります。
しかし、Excel があればわざわざ Web ブラウザーを起動してサイトにアクセスする必要はありません。
Excel を起動して方眼紙を作るだけでドット絵を描き始めることができます。

まず手始めに、ドット絵の代表格であるマリオを描いてみます。

[塗りつぶしの色] – [その他の色] で色を設定して、セルの背景色を設定します。

塗りつぶしの色色の設定

セルの右下のハンドルをドラッグすれば、連続する領域を塗りつぶせます。

オートフィル

できました。

マリオ (Excel 方眼紙)

サイズは 16 x 16 で、この程度であれば 2~3 分で描けることと思います。

せっかくなので、このドット絵を画像ファイルに変換したいと思います。
というわけで、Dot Excel というコマンドライン ツールを作成しました。
CodePlex で公開しており、リポジトリとして Git を選択しています。

このツールでは、Open XML SDK 2.5 を使用しています。
以前から Office オートメーションという、素人が利用するとは考えにくい方法も存在するのですが、
Open XML 形式のファイル (.xlsx) を対象とする場合には Open XML SDK を利用できます。
この他に、高度で専門的な Excel マクロというものを利用する方法もあるようです。

Open XML SDK についての解説は、後日に書く予定です。

さて、先ほどの Excel ファイルを保存して終了し、エクスプローラー上でドラッグして DotExcel.exe にドロップします。

Excel から PNG へ変換

ドラッグ & ドロップ操作で実行した場合は PNG ファイルが作成されるようになっています。
また、塗りつぶしが設定されなかった部分は透明になります。

変換された PNG ファイル

小さいので、Windows フォト ビューアーで拡大します。
拡大しても滑らかな曲線とならずに済んでいます。

マリオ (変換後)

現在の Dot Excel では Excel から画像への変換機能しかありませんが、じゃあ画像から Excel への変換機能をいつ作るか?
そのうち時間ができたら作ろうと思います。

 

では、もっと大きなドット絵の作成に挑戦してみましょう。
題材は Silverlight の藍澤光さんです。

藍澤光

Microsoft Silverlight 個人收藏 – Windows Phone 7.5 桌布下載 480 X 800

実は、当初は本来のサイズである 480 x 800 で作り始めたのですが、
どうやら色数が 65536 を超えると (?) Excel でエラーになるらしく、途中で 240 x 400 にして作り直しました。
まさかの心折設計。

気を取り直して描き上がったものがこちら。拡大率 20%。

藍澤光 (Excel 方眼紙 20%)

拡大率 70%。

藍澤光 (Excel 方眼紙 70%)

プロの Excel 方眼師の方によれば、
この Excel 方眼紙は大規模プロジェクトにおけるガントチャートやニコニコカレンダーに似ているとのことです。

Excel 方眼紙でニコニコカレンダーというのは、こうでしょうか、わかりません。

ニコニコカレンダー

これを見て、2025 年に放送が開始される予定の「アタック2025」のイメージ画像 (画像イメージ?) を思い出しました。
「アタック2025」では巨大な液晶タッチパネル (45 x 45) が必要となるため、シャープがスポンサーとなることが期待されています。

さて、この Excel 方眼紙を Dot Excel で JPEG ファイルに変換します。
JPEG ファイルに変換するには、コマンドラインでオプションを指定して実行します。

dotexcel Hikaru.xlsx /format:jpg

Excel から JPEG へ変換

ひかるたんの JPEG ファイルが生成されました。

ひかるたん (変換後)

 

このように、Open XML SDK を利用してさまざまな業務を自動化できます。
Excel 方眼紙を使って社内で出世したいのにネタがなくてお悩みの方は、 
とりあえず Excel 方眼紙の背景色からソースコードを自動生成するツールとか作ってみればいいと思います。

バージョン情報
Excel 2010
.NET Framework 4

参照
Dot Excel (CodePlex)
Open XML SDK 2.5
Open XML for Office developers
Open XML SDK で Excel シートを読み込む

Excel方眼紙 Advent Calendar 2013
『Excel(エクセル)で○○してみた』スゴイ動画集【裏技&小ネタ】 (NAVER まとめ)
エクセル・ドット・アートのクオリティが高すぎる
Microsoft – Silverlight 桌布下載

// Have a nice April.

カテゴリー: .NET Framework, ツール. タグ: , . 2 Comments »