それではそろそろ本題へ入っていきます。

まずは基本中の基本、セルのデータをセットする方法について取り組みます。

Excelファイルでのセルデータの表現

いきなりサンプルコードをお見せしてもいいのかもしれませんが、そうしてしまうとネタがすぐに尽きてしまうので まずはExcelファイルにはどのようにセルデータが格納されているのかについて見ていきます。

Excelを起動して、新規ブックに以下のようなデータを入力します。

文字と漢字(フリガナつき)と数値と日付をそれぞれ入力します。名前を付けて(Book1.xlsx)デスクトップ等適当なところに保存します。

こうして作ったBook1.xlsxの中身をProductivity Toolを使って覗いてみます。

もちろん拡張子をzipに代えて、解凍して中身を覗いてみても構いません。Productivity Toolがなかった時(まだ存在を知らなかったとき?)には私もそうしていました。

Document Explorerに見える「Book1.xlsx」ノードから順に「/xl/workbook」「/xl/worksheets/sheet1.xml」とノードを展開していき、「x:worksheet (Worksheet)」をクリックして選択します。(下図1)

ツールバーの「Reflect Code」ボタンをクリックすると、右上のペインに「/xl/worksheets/sheet1.xml」の、「x:worksheet」要素を表示し、これを作成するためのサンプルコード(C#)を右下のペインに表示してくれます。

XMLでシートについてのいろいろが記述されています。pageMargins (ページ余白)など、タグ名を見るとなんとなく意味が読み取れると思います。

で、今回注目すべきはセルのデータについての記述です。

 <x:sheetData>
    <x:row r="1" spans="1:1" x14ac:dyDescent="0.15">
      <x:c r="A1" t="s">
        <x:v>0</x:v>
      </x:c>
    </x:row>
    <x:row r="2" spans="1:1" x14ac:dyDescent="0.15">
      <x:c r="A2" t="s">
        <x:v>1</x:v>
      </x:c>
    </x:row>
    <x:row r="3" spans="1:1" x14ac:dyDescent="0.15">
      <x:c r="A3">
        <x:v>12345.678900000001</x:v>
      </x:c>
    </x:row>
    <x:row r="4" spans="1:1" x14ac:dyDescent="0.15">
      <x:c r="A4" s="1">
        <x:v>42173</x:v>
      </x:c>
    </x:row>
  </x:sheetData>

 タグ名の先頭についてるx:は、名前空間の接頭辞ですのでここでは注目する必要はありません。

sheetData要素のなかに「行」を記述するrow要素があって、それぞれの行に「セル」を記述するc要素があることが見て取れます。

row要素のr属性は行番号であることは容易に想像できるとおもいます。c要素のr属性も同様にセル番地が記述されています。

c要素の中には「値」を記述するv要素が格納されています。

ということで、上記の要素をそのまま読むと、

セルA1には「0」, セルA2には「1」, セルA3には「12345.678900000001」, セルA4には「42173」が入っている

ということになります。

セルA3は問題ないですね?

セルA4は日付のはずですが、ここには日付「2015/6/18」の日付シリアル値である「42173」が入っています。

よくわからないのは文字が入っているはずのセルA1, A2の値「0」「1」ですね?

セルA1, A2のt属性が"s"となっていますが、これによってこれらセルの値は、SharedStringのインデックス番号であることがわかります。

SharedStringはブック内の文字列セルで共有する文字列を集中管理するファイルです。「/xl/workbook」「/xl/sharedStrings.xml」とノードを展開していき、「x:sst (SharedStringTable)」をクリックして選択するとその中身を確認することができます。

<x:sst count="2" uniqueCount="2" xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
  <x:si>
    <x:t>セルA1</x:t>
    <x:phoneticPr fontId="1" />
  </x:si>
  <x:si>
    <x:t>東京特許許可局</x:t>
    <x:rPh sb="0" eb="2">
      <x:t>トウキョウ</x:t>
    </x:rPh>
    <x:rPh sb="2" eb="4">
      <x:t>トッキョ</x:t>
    </x:rPh>
    <x:rPh sb="4" eb="7">
      <x:t>キョカキョク</x:t>
    </x:rPh>
    <x:phoneticPr fontId="1" />
  </x:si>
</x:sst>

si要素がそれぞれブック内で共有されている文字列の情報です。rPh要素はフリガナです。sb属性, eb属性はそれぞれフリガナを振り始める文字区切りの位置(最初の文字の直前が0)、フリガナを振る終わりの文字区切りの位置です。

si要素が2つ含まれていることが確認できますが、先のインデックス番号はそれぞれ「0番目のsi」、「1番目のsi」という意味です。(最初のsi要素のインデックスが0番で、次のsi要素のインデックスが1番です)

これによって先のSheet1のセルA1には文字列「セルA1」が入り、セルA2には文字列「東京特許許可局」が入ることになります。

説明が長くなりましたが、以上からセルにデータをセットするには、おおよそ以下のステップを踏まなくてはならないことがわかります。

  1. 文字をセットするシート(Worksheetパーツ)を特定する。
    worksheet パーツのセル(c要素)を特定する。このときc要素がなければc要素を追加する。
    そのためには行(r要素)を特定する必要がある。このときr要素がなければr要素を追加する。
  2. 特定したセル(c要素)にデータをセットする。ただし文字データをセットする場合にはSharedStringパーツからセットしたい文字列を検索して、そのインデックス番号(n番目のsi要素)を取得する。
    (このときセットした文字列をもつsi要素が存在しなければ新たにsi要素を追加し、そのインデックス番号を取得する。)
    取得したインデックス番号を取得したc要素にセットする

このステップをプログラムとして記述していくことになります。

プログラムを作成するに当たっては、「要するにXMLを編集するプログラムを書く!」ということになるわけですが、これを極力(あくまで「極力」...) 楽にしてくれるのがOpen XML SDKです。

c要素のt属性に"str"を指定すると、直接文字列をセットすることができます。つまりセルA1, A2については

   <x:row r="1" spans="1:1" x14ac:dyDescent="0.15">
      <x:c r="A1" t="str">
        <x:v>セルA1</x:v>
      </x:c>
    </x:row>
    <x:row r="2" spans="1:1" x14ac:dyDescent="0.15">
      <x:c r="A2" t="str">
        <x:v>東京特許許可局</x:v>
      </x:c>
    </x:row>

 のように記述することができます。

ただしt属性に"str"を指定したファイルをExcelで開いて、そのまま閉じようとすると(何も編集していないにもかかわらず)Excelに「変更を保存するかどうか?」を問われます。

変更を保存してProductivity Toolで中身を確認すると、文字列はすべてSharedStringパーツに移動されてしまっていることがわかります。(c要素のt属性も"s"に変更されて、値(v要素)にはSharedStringのsi要素のインデックス番号がセットされます)

Excelのこのふるまいをよしとするならセルに文字列をセットするプログラムはうんと楽になります。

SharedStringってめんどくさいのぉ、と思いませんか?

どこで読んだのか忘れてしまったのですが、「Excelファイルの内容の翻訳を容易にするために導入された」というような説明がされていたのを覚えています。

つまり、あるExcelファイルのセルの文字データを別の言語に翻訳したければsharedStrings.xmlを抜き出して、その中の文字列をすべて翻訳し、これを書き戻すだけでOK!ということらしいです。
まぁその考えもわからないではないですが...