38
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Excelの実体って結局何なのか?が気になった話

38
Posted at

はじめに

普段Pythonでopenpyxlpandasを使ってExcelを触っていて、ふと「このライブラリたち、内部でいったい何をやってるんだろう?」と気になったことはありませんか?

私は気になりました。気になって調べたら、思った以上にシンプルで、しかも応用範囲が広い話だったので記事にまとめます。

対象読者

  • Pythonなどから.xlsxファイルを操作するライブラリを使っている中級エンジニア
  • Office文書の「中身」を一度ちゃんと知っておきたい人
  • 「ClaudeがなぜExcelを編集できるのか」が気になる人

結論

  • .xlsxの正体はZIPで圧縮されたXMLファイルの集合
  • これはOPC (Open Packaging Conventions) という業界標準パターンで、.docx/.pptx/.epub/.apk/.jarも全部同じ仕組み
  • ZIPが選ばれたのは「圧縮率」「複数ファイルの集約」「特許フリー」「ランダムアクセス」の4点が揃っていたから
  • 中身がXMLテキストだから、Claudeなどの言語モデルでも編集できる

拡張子を .zip に変えてみる

百聞は一見にしかず、です。手元の.xlsxファイルをコピーして、拡張子を.zipに変えてみてください。普通に解凍できます。

cp sample.xlsx sample.zip
unzip sample.zip -d sample_extracted

中身はこんな構造になっています。

sample.xlsx/
├── [Content_Types].xml          ← 各ファイルのMIMEタイプ定義
├── _rels/
│   └── .rels                    ← パッケージ全体のリレーション
├── docProps/
│   ├── app.xml                  ← アプリ情報(作成者、Excelバージョン等)
│   └── core.xml                 ← メタデータ(タイトル、更新日時等)
└── xl/
    ├── workbook.xml             ← シート一覧、定義名など
    ├── styles.xml               ← セル書式(フォント、罫線、色)
    ├── sharedStrings.xml        ← 文字列の共通プール
    ├── _rels/
    │   └── workbook.xml.rels    ← workbookからの参照関係
    └── worksheets/
        ├── sheet1.xml           ← Sheet1の中身(セルの値)
        └── sheet2.xml           ← Sheet2の中身

私は初めてこれを見たとき、率直に「あ、こんなにシンプルなのか」と思いました。Excelファイルというと何か魔法のバイナリを想像していたのですが、実態はただのテキストファイルの寄せ集めだったのです。

中身の正体は Open Packaging Conventions (OPC)

このZIP+XMLという形式、Microsoftが思いつきで作った独自仕様ではありません。OPC (Open Packaging Conventions) という規格として整理されていて、ISO/IEC 29500で標準化されています1

そして驚くべきことに、同じ仕組みを使っているファイル形式は他にもたくさんあります。

拡張子 中身
.xlsx / .xlsm Excel
.docx Word
.pptx PowerPoint
.odt / .ods / .odp LibreOffice (OpenDocument)
.epub 電子書籍
.jar / .war Java アーカイブ
.apk Android アプリ
.ipa iOS アプリ

全部ZIPです。手元の.apk.epubの拡張子を.zipに変えてみると本当に解凍できます。私もこの記事を書きながら試してみて、ちゃんと開けたときは少し感動しました。

つまり「ZIPに構造を決めてアプリの保存形式にする」というのは、ソフトウェア業界ではごく普通のパターンなんです。Microsoftが独自に編み出したわけではなく、すでに実績のあった手法を踏襲した結果と言えます。

※厳密に言うと、全部同じ仕組みというのは少し乱暴みたいですが、今回はざっくり理解が目的なので割愛します。

なぜZIPが選ばれたのか

ではなぜ、Office文書の保存形式にZIPが選ばれたのでしょうか。理由は4つあります。

1. 圧縮率が高い

XMLは見ての通り、タグの分だけ冗長です。セル1個でも<c r="A1" t="s"><v>0</v></c>のような記述が必要で、これが何万セル分も並びます。

ZIPの圧縮アルゴリズム(Deflate)はXMLとの相性が抜群で、繰り返しパターンを効率的に圧縮します。実測で元サイズの10〜20%程度まで縮むことも珍しくありません。

2. 複数ファイルを1つにまとめられる

Excelファイルは「ワークブック定義」「各シート」「スタイル」「文字列プール」など、機能ごとにファイルを分離したい事情があります。これを1つの.xlsxとしてユーザーに届けるには、アーカイブの仕組みが必要です。

3. 特許フリー・枯れた技術

ZIPは1989年から存在する仕様で、ライブラリも豊富、ライセンス問題もありません。新しいフォーマットの基盤として採用するうえで、これ以上ない安心感です。

4. ランダムアクセス可能

ZIPは中央ディレクトリ構造を持つため、「シート3だけ読みたい」という部分アクセスが可能です。tarだと頭から走査が必要なので、この差は大きいです。

image.png

sharedStrings.xml という地味だが賢い工夫

中身を覗いていて私が「お、これは賢い」と感じたのがsharedStrings.xmlの仕組みです。

セルに"東京"という文字列を100個書いても、実体はsharedStrings.xmlに1回だけ格納され、各セルはそのインデックス番号を参照する設計になっています。

sharedStrings.xml
<sst>
  <si><t>東京</t></si>
  <si><t>大阪</t></si>
</sst>
sheet1.xml
<c r="A1" t="s"><v>0</v></c>  <!-- "東京"を参照 -->
<c r="A2" t="s"><v>1</v></c>  <!-- "大阪"を参照 -->

t="s"がshared string参照、t="n"(または属性なし)が数値です。数値はインライン、文字列は参照プールから引くという設計になっています。

openpyxlでExcelを読むとき、内部ではこのsharedStrings.xmlをパースしてメモリに展開し、各セルの<v>タグの値をキーとして文字列を引き直しています。大きなファイルでread_only=Trueモードが推奨されるのも、このプール構造を一気にメモリに乗せないためです。

.xlsx / .xlsm / .xlsb の違い

ついでに、Excelの保存形式の親戚たちも整理しておきます。

拡張子 構造 特徴
.xlsx ZIP+XML 標準形式、マクロなし
.xlsm ZIP+XML + xl/vbaProject.bin マクロ付き、バイナリのVBAが追加される
.xlsb バイナリ形式 XMLではない。巨大ファイルで高速だが汎用ツールから扱いにくい

業務でPythonから扱うときは基本.xlsxで十分ですが、サイズが数百MBを超えてくると.xlsbへの変換が現実的な選択肢になります。ただし.xlsbはopenpyxlでは読めないので、pyxlsbなどの専用ライブラリが必要です。

「ZIPに名前を付けただけ」の限界

ここまで読むと「じゃあ拡張子変えれば何でも.xlsxとして開けるの?」と思いますよね。私も最初そう思いました。

でも、そうはいきません。中身のXMLが厳密な仕様(ISO/IEC 29500、約6000ページ)に従っている必要があります

  • [Content_Types].xmlが必須
  • xl/workbook.xmlのスキーマに従う
  • リレーションシップ(.rels)で参照関係を正しく記述
  • セルの座標、データ型、参照の整合性

なので正確には「ZIPをコンテナとして使い、その中身の構造を仕様で定めたもの」です。コンテナとしてのZIPは汎用品、中身のフォーマットは専用品、という二層構造になっています。

.xlsxの中のXMLを手で書き換えると、整合性が崩れてExcelが「ファイルが壊れています」と言ってくることがあります。書き換える場合は必ずバックアップを取り、[Content_Types].xml.relsの整合性に注意してください。

だからClaudeは.xlsxを編集できる

ここが個人的に一番おもしろいと感じた点です。

.xlsxがZIP+XMLという構造である事実は、LLM時代になって新しい意味を持ち始めたと思っています。

考えてみてください。もし.xlsxが旧形式の.xlsのような独自バイナリだったら、ClaudeのようなLLMがExcelファイルを編集するには、

  1. バイナリ仕様を完全に理解し
  2. オフセット計算をミスなくこなし
  3. チェックサムを正しく計算する

…という、言語モデルが本質的に苦手とする処理が必要になります。

でも実際には、.xlsxの中身は人間が読めるXMLテキストです。Claudeから見れば、ZIPを解凍してXMLを編集して、ZIPに戻すだけ。これは「テキスト処理」の延長線上にあります。

claude_edits_xlsx.py
# Claudeが実際にやっていること(概念図)
import zipfile
import shutil

# 1. ZIPとして解凍
with zipfile.ZipFile('input.xlsx', 'r') as z:
    z.extractall('workdir')

# 2. XMLをテキストとして編集
with open('workdir/xl/worksheets/sheet1.xml', 'r') as f:
    content = f.read()
content = content.replace('<v>old_value</v>', '<v>new_value</v>')
with open('workdir/xl/worksheets/sheet1.xml', 'w') as f:
    f.write(content)

# 3. ZIPに戻す
shutil.make_archive('output', 'zip', 'workdir')
shutil.move('output.zip', 'output.xlsx')

実際にはClaude Code等の内部ではopenpyxlのようなライブラリを呼んでいることが多いですが、根本にあるのは「XMLテキストの編集」という、LLMが得意な作業です。

2000年代にMicrosoftがOOXMLを策定したとき、彼らは「相互運用性のため」と説明しました。EUからの圧力もありました。でも結果として、その決定は20年後にLLMがOffice文書を扱えるようになるという、誰も予想していなかった副産物を生んだのです。

技術選択の長期的な影響って、こういうところに現れるんだなと、調べていてしみじみ感じました。

まとめ

要点を整理します。

  • .xlsxの正体はZIP圧縮されたXMLファイルの集合
  • これはOPCという業界標準パターン。.docx/.pptx/.epub/.apk/.jarも同じ
  • ZIPが採用されたのは「圧縮率」「集約」「特許フリー」「ランダムアクセス」の4点
  • sharedStrings.xmlによる文字列プール化が処理速度の鍵
  • 中身がXMLテキストだから、LLMでも編集できる

データエンジニアの実用的な含意としては、

  • 壊れたExcelの救出: 解凍してXMLを直接編集すれば、Excelで開けないファイルから値を救出できることがある
  • 大量生成の高速化: openpyxlを介さず、テンプレートZIPの一部だけ差し替える方法で高速化が可能
  • Git管理は厳しい: ZIPなのでバイナリ扱いになり、差分が取れない。テキスト管理したいならCSVに分離するのも手

ファイル形式の歴史と中身を知ると、なぜこの設計なのかが腑に落ちます。私は今後Excelを触るとき、ちょっと違う目で見られそうです。

もし手元に.xlsxがあったら、ぜひ拡張子を.zipに変えて中身を見てみてください。実際にやってみると一気に解像度が上がります。

  1. ISO/IEC 29500: Information technology — Document description and processing languages — Office Open XML File Formats

38
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
38
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?