はじめに
今回はエクセルのファイル(.xlsx
)からライブラリを用いずに中身を調べる手法について紹介をしたいと思います。
Unityでエクセルのデータを扱おうとすると、NPOI
というライブラリを導入することが多いようですね。
www.nuget.org
github.com
ただ最近.xlsx
はただの.zip
ファイルだよということを知りまして、面白そうなので自分でもやってみようかなと思いました。
思ったより簡単にできたので紹介します。
.xlsxについて
.xlsx
はzip
で圧縮されており、展開するとxml
等の中身が出てきます。
実際に以下のエクセルデータを作成し、展開してみました。
エクセル名.xlsx |---- _rels | |---- .rels |---- [Content_Types].xml |---- docProps | |---- app.xml | |---- core.xml |---- xl |---- _rel | |---- workbook.xml.rels |---- theme | |---- theme1.xml |---- worksheets | |---- sheet1.xml |---- sharedStrings.xml |---- styles.xml |---- workbook.xml
.xlsx
はOffice Open XML
というオフィス用のファイルフォーマットを採用しているファイルになります。
Office Open XML (OpenXML、OOXML) とは、XMLをベースとし、Microsoftが策定、最初はECMAで引き続きISO/IEC JTC1により標準化されたオフィススイート用ファイルフォーマットである。
...
Microsoft Office はデータを保存するにあたり独自のバイナリ形式を用いてきたが、バージョン12(Office 2007)からは、XMLで記述された規格を標準ファイル形式として採用した。それが Office Open XMLである。
SpreadsheetML
という奴ですね。
SpreadsheetML
Excelなど表計算のデータを記述するための言語。ワークブックの下に複数のワークシートが連なるという形で構成される。
C#で要素の一覧を取得する
エクセルに記述された文字列はxl/sharedStrings.xml
に入っているようですね。
// sharedStrings.xmlの中身 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="14" uniqueCount="14"> <si> <t>これはA1です。</t> <phoneticPr fontId="1" /> </si> <si> <t>これはA4です。</t> <phoneticPr fontId="1" /> </si> <si> <t>これはB1です。</t> <phoneticPr fontId="1" /> </si> <si> <t>B2</t> <phoneticPr fontId="1" /> </si> <si> <t>A1</t> <phoneticPr fontId="1" /> </si> <si> <t>A3</t> <phoneticPr fontId="1" /> </si> <si> <t>A5</t> <phoneticPr fontId="1" /> </si> <si> <t>A6</t> <phoneticPr fontId="1" /> </si> <si> <t>A7</t> <phoneticPr fontId="1" /> </si> <si> <t>A8</t> <phoneticPr fontId="1" /> </si> <si> <t>B3</t> <phoneticPr fontId="1" /> </si> <si> <t>B4</t> <phoneticPr fontId="1" /> </si> <si> <t>B5</t> <phoneticPr fontId="1" /> </si> <si> <t>C1</t> <phoneticPr fontId="1" /> </si> </sst>
この中身を取得したい場合はZipFile
クラスを利用することで簡単にできます。
ZipFile クラス (System.IO.Compression) | Microsoft Learn
// 適当にStreamingAssetsの中に「Sample.xlsx」を入れておく var zipPath = Application.streamingAssetsPath + "/Sample.xlsx"; using var archive = ZipFile.Open(zipPath, ZipArchiveMode.Update); var entry = archive.GetEntry("xl/sharedStrings.xml"); if (entry != null) { using var reader = new StreamReader(entry.Open()); Debug.Log(reader.ReadToEnd()); }
どのセルの要素かを調べる
sharedStrings.xml
にはセルに記述された文字列の一覧が格納されています。
異なるセルに重複した文字列を記述したとしても、sharedStrings.xml
には一つしか書き込まれません。賢いですね。
ではどのセルにどの文字列が書かれているかといった情報はどこにあるのかというと、xl/worksheets/sheet〇.xml
(sheet
名と一致するわけではないよう)に格納されています。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac xr xr2 xr3" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" xmlns:xr="http://schemas.microsoft.com/office/spreadsheetml/2014/revision" xmlns:xr2="http://schemas.microsoft.com/office/spreadsheetml/2015/revision2" xmlns:xr3="http://schemas.microsoft.com/office/spreadsheetml/2016/revision3" xr:uid="{EEFCE81E-973B-AF43-8966-800BEF7D3AA3}"> <dimension ref="A1:C8" /> <sheetViews> <sheetView tabSelected="1" workbookViewId="0"> <selection activeCell="C1" sqref="C1" /> </sheetView> </sheetViews> <sheetFormatPr baseColWidth="10" defaultRowHeight="20" /> <cols> <col min="1" max="1" width="35.42578125" customWidth="1" /> <col min="2" max="2" width="26.85546875" customWidth="1" /> </cols> <sheetData> <row r="1" spans="1:3"> <c r="A1" t="s"> <v>0</v> </c> <c r="B1" t="s"> <v>2</v> </c> <c r="C1" t="s"> <v>13</v> </c> </row> <row r="2" spans="1:3"> <c r="A2" t="s"> <v>4</v> </c> <c r="B2" t="s"> <v>3</v> </c> </row> <row r="3" spans="1:3"> <c r="A3" t="s"> <v>5</v> </c> <c r="B3" t="s"> <v>10</v> </c> </row> <row r="4" spans="1:3"> <c r="A4" t="s"> <v>1</v> </c> <c r="B4" t="s"> <v>11</v> </c> </row> <row r="5" spans="1:3"> <c r="A5" t="s"> <v>6</v> </c> <c r="B5" t="s"> <v>12</v> </c> </row> <row r="6" spans="1:3"> <c r="A6" t="s"> <v>7</v> </c> </row> <row r="7" spans="1:3"> <c r="A7" t="s"> <v>8</v> </c> </row> <row r="8" spans="1:3"> <c r="A8" t="s"> <v>9</v> </c> </row> </sheetData> <phoneticPr fontId="1" /> <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3" /> </worksheet>
このv
タグで囲まれた数字が、sharedStrings.xml
に格納されていた文字列の順番に該当します。
// 適当にStreamingAssetsの中に「Sample.xlsx」を入れておく var zipPath = Application.streamingAssetsPath + "/Sample.xlsx"; using var archive = ZipFile.Open(zipPath, ZipArchiveMode.Update); var sharedStrings = archive.GetEntry("xl/sharedStrings.xml"); if (sharedStrings != null) { using var reader = new StreamReader(sharedStrings.Open()); Debug.Log(reader.ReadToEnd()); } var sheet1 = archive.GetEntry("xl/worksheets/sheet1.xml"); if (sheet1 != null) { using var reader = new StreamReader(sheet1.Open()); Debug.Log(reader.ReadToEnd()); }
補足
xml
の解析は色々と手法があり、派閥もあるらしいので今回は割愛します。