Go xlsファイルを読み取る
今回は、Goで Excel の xlsファイル(Excel 97 ~ Excel 2003 ブック) を読み取る方法を考えてみます。
xlsxファイルの場合は、 excelize がオススメです。
ライブラリ
簡単に読み取りを行うなら、下記のライブラリを使うとよいです。
GitHub - extrame/xls: Pure Golang xls library
https://github.com/extrame/xls
↓ Example を参考に書いてみるとこんな感じ
package main
import (
"fmt"
"log"
"github.com/extrame/xls"
)
func main() {
xlFile, err := xls.Open("input.xls", "utf-8")
if err != nil {
log.Fatal(err)
}
sheet1 := xlFile.GetSheet(0)
if sheet1 != nil {
fmt.Printf("Total Lines : %d (%s)\n", sheet1.MaxRow, sheet1.Name)
for row := 0; row < int(sheet1.MaxRow); row++ {
// sheet から 行 を取り出す
r := sheet1.Row(row)
for col := r.FirstCol(); col < r.LastCol(); col++ {
// 行(row) と 列(col) を指定すると 値 が返ってくる
value := sheet1.Row(row).Col(col)
// 下記でもよい
// value := r.Col(col)
fmt.Printf("(%d, %d) = %s\n", row, col, value)
}
}
}
}
詳しくは、下記をご覧ください。
xls - GoDoc
https://godoc.org/github.com/extrame/xls
2020/06/01 時点では、うまく読み込めないものがあります。
うまく読み込めないもの
数式の含まれたファイル
例えば 参照セル =A2
というような数式となっていると、紹介したライブラリでは読めなさそう?
※数式の入ったセルの値が空文字となる
パスワードがかかったもの
パスワードかかったファイルの読み込みには対応していない
なんとかして読み込む
そんなファイルたちをOLEで読み込んでみましょう。
Object Linking and Embedding - Wikipedia
https://ja.wikipedia.org/wiki/Object_Linking_and_Embedding
ただOLEを使う場合、デメリットもあります。
- 動作速度が遅い
- 命令を送ってアプリケーションを操作してというような処理なのであまり早くはならない
- サーバーサイドで使いにくい
- Excelを直接動かすため、サーバーサイドでは使いにくいですね
- メモリやパワーがいる
- 裏でExcelが立ち上がって、なんやかんやするのでそれなりに負荷はあります
実装
ただ値を取りたいだけなのですが、かなりめんどくさい処理になってます。
※いい方法があったら教えてください
単にちょっとだけ情報取りたいだけなら
OLEで完結させてもいいかもしれません。
※OLE経由でセル情報を取得することができる
すべてのセルを見たい場合は、
別の扱いやすいファイルに直したほうが早いかなと思ったので
以下の方法を考えてみました。
- テンポラリファイルパスを作る (ioutil.TempFile)
- Excelを立ち上げる (OLE)
- xlsファイルを開く (OLE)
- csvファイル(テンポラリ)へ保存する (OLE)
- csvファイル(テンポラリ)を開く (encoding/csv)
別にcsvファイルでなくてもよいのですが、
公式packageにcsv読み込めるものがあるので利用しました。
出来上がったソースが、以下です。
※エラーとかあまり考慮してない
$ go build & xxx.exe xxx.xls
な感じで読み込みできます。
実行には、Excelが必要です。
package main
import (
"encoding/csv"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"github.com/mattn/go-ole"
"github.com/tanaton/go-ole-msoffice/excel"
"golang.org/x/text/encoding/japanese"
"golang.org/x/text/transform"
)
func main() {
args := os.Args
rows, err := getXlsData(args[1])
if err != nil {
log.Fatal(err)
}
fmt.Printf("%#v", rows)
}
func getXlsData(path string) ([][]string, error) {
var rows [][]string
// テンポラリファイルパス作成
dist, err := ioutil.TempFile("", "*.csv")
if err != nil {
return rows, err
}
dist.Close()
os.Remove(dist.Name())
// 処理後削除
defer os.Remove(dist.Name())
// OLE
ole.CoInitializeEx(0, ole.COINIT_APARTMENTTHREADED|ole.COINIT_DISABLE_OLE1DDE)
defer ole.CoUninitialize()
// Excelを取得する
e := excel.ThisApplication()
if e == nil {
return rows, nil
}
// Excel設定: アラートの表示(true: する、false: しない)
e.SetDisplayAlerts(false)
// Excel設定: Excel自体の表示(true: する、false: しない)
e.SetVisible(false)
workbooks := e.GetWorkbooks()
// 必ず絶対パスにする
absPath := path
if !filepath.IsAbs(absPath) {
absPath, err = filepath.Abs(path)
if err != nil {
return rows, err
}
}
// Excelファイルを開く
book := workbooks.Open(absPath)
// csvファイルとして保存する
book.SaveAs(dist.Name(), excel.XlCSV)
// ファイルとExcelを閉じる
book.Close()
e.Quit()
e.Release()
// csvファイルを開いて読み込む
f, err := os.Open(dist.Name())
if err != nil {
return rows, err
}
defer f.Close()
r := csv.NewReader(transform.NewReader(f, japanese.ShiftJIS.NewDecoder()))
return r.ReadAll()
}
パスワード付きを読み込む場合
下記のようにすれば、パスワードがかかったものも読めます。
// Open(FileName, UpdateLinks, ReadOnly, Format, Password)
book := workbooks.Open(src, 0, true, 5, password)
Open メソッド (Excel) | Microsoft Docs
https://docs.microsoft.com/ja-jp/office/vba/api/excel.workbooks.open
まとめ
OLEを使って xlsファイルの読み込みをやってみました。
OLEを使うことによって、いろんなことができると思うので試してみてくださいね。
xlsファイルだけじゃなくてExcelで扱えるファイルは、OLE経由で読めるはず。
あと いい方法・いいpackageあったら教えてください (o*。_。)o