いろいろ備忘録日記

主に .NET とか Go とか Python絡みのメモを公開しています。

Goメモ-122 (別ファイルのExcelシートをコピーしてもってくる)

概要

以下、自分用メモも兼ねています。

以前にExcel関連の処理をGoからいろいろ操作したりした記事を何個かアップしていました。

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

このときは自前のライブラリを使って、ごにょごにょしてたのですが、GoのExcelライブラリで有名な

github.com

とか

github.com

の方が使いやすいし楽だしで、最近ずっとそっち使っていました。

シートコピーする関数がない

んで、ひょんなことで、特定のフォルダの下にいっぱいあるExcelファイルの各シートを一つのExcelに纏めたいということがありました。

(章毎にフォルダが作られてて、その中に項番毎のフォルダがさらにあって、その中にExcelがあって、シートになんか画像とか結果とかが入ってると想像してください・・・)

おけ、んじゃ作るかってなって xlsx と excelize を使おうとおもったら、これらにシートをコピーする関数ってないんですね・・・。

excelizeの方には以下のようなissueがあって、まだopen状態でした。

github.com

xlsxの方もなんか情報ないかなーって探しみたら以下を発見。

jenicaandpatrick.com

見てみると、元のシートから各セルのデータ全部取得して持っておいて、コピー先のシートに全部書き込んでいくっていうサンプルでした。

これ、データがテキストとかだけだったらいいですが、画像とか表とかだったらちょっと厳しいですね。スタイルとかもなくなっちゃうし。。。

GoはやめてPythonでスクリプト書いちゃおう

んじゃ、pythonで作ろうって思って openpyxl のドキュメントみてたら copy_worksheet って関数あって、よく見ると以下の文言が。。。

This function cannot copy worksheets between workbooks. worksheets can only be copied within the workbook that they belong

openpyxl.readthedocs.io

別ファイルにあるシートをコピーするのは出来ないみたい。。残念。

VBAだったら、ワークシートにCopyって関数があるので、楽勝なんですけどね。。。やっぱりGoとかでやってみたいじゃんw

自前ライブラリにシートコピー関数を追加する

無いんだったら、自前のライブラリの方に追加しようってことにしました。

私のライブラリは go-ole を使ってExcel触っているので、VBAで出来ることはほぼ出来るはず。

てことで、以下の関数を追加。

func (ws *Worksheet) CopySheet(dest *Worksheet, after bool) error

中は、go-ole 経由でCopy関数呼び出しているだけですが。go-ole 素晴らしい。

サンプル

以下、サンプルです。スクリプトみたいな感覚で一度動けばいいのでエラー処理とかはほぼ無視しています。

package main

import (
    "flag"
    "log"
    "os"
    "path/filepath"
    "strings"

    "github.com/devlights/goxcel"
)

// flag parameters
var (
    srcDir string
    out    string
)

// logs
var (
    wsLog = log.New(os.Stdout, ">>> ", 0)
)

func main() {
    os.Exit(run())
}

func run() int {
    flag.StringVar(&srcDir, "srcdir", "", "source directory")
    flag.StringVar(&out, "out", "result.xlsx", "output file name")
    flag.Parse()

    if srcDir == "" {
        flag.Usage()
        return 1
    }

    quitFn, _ := goxcel.InitGoxcel()
    defer quitFn()

    g, r, _ := goxcel.NewGoxcel()
    defer r()

    _ = g.SetDisplayAlerts(false)
    _ = g.SetVisible(false)

    wbs, _ := g.Workbooks()
    wbDest, wbDestR, _ := wbs.Add()
    defer wbDestR()

    wsDest, _ := wbDest.Sheets(1)
    _ = filepath.Walk(srcDir, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }

        if info.IsDir() {
            return nil
        }

        absPath, _ := filepath.Abs(path)
        if !strings.HasSuffix(absPath, "xlsx") {
            return nil
        }

        wsLog.Println(path)

        wb, wbr, _ := wbs.Open(absPath)
        defer wbr()

        wss, _ := wb.WorkSheets()
        _, err = wss.Walk(func(ws *goxcel.Worksheet, index int) error {
            err := ws.CopySheet(wsDest, false)
            if err != nil {
                return err
            }

            return nil
        })

        return err
    })

    wd, _ := os.Getwd()
    curdir, _ := filepath.Abs(wd)
    resultPath := filepath.Join(curdir, out)

    err := wbDest.SaveAs(resultPath)
    if err != nil {
        log.Println(err)
        return 2
    }

    return 0
}

実行すると、-srcdir で指定したディレクトリ配下の全Excelファイルを開けて、その中のシートを全部コピーして一つのExcelファイルに集約します。

github.com

とりあえず、自分の望みの結果は手に入ったのでスッキリ。

おすすめ書籍

自分が読んだGo関連の本で、いい本って感じたものです。

Go言語による並行処理

Go言語による並行処理

スターティングGo言語 (CodeZine BOOKS)

スターティングGo言語 (CodeZine BOOKS)

  • 作者:松尾 愛賀
  • 発売日: 2016/04/15
  • メディア: 単行本(ソフトカバー)

プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)

プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)


過去の記事については、以下のページからご参照下さい。

  • いろいろ備忘録日記まとめ

devlights.github.io

サンプルコードは、以下の場所で公開しています。

  • いろいろ備忘録日記サンプルソース置き場

github.com

github.com

github.com