いろいろ備忘録日記

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

Goメモ-472 (複数のxlsxファイルをGREPする)

関連記事

GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ

概要

以下、自分用のメモです。

昨日のメモで、docxファイルをGREPする記事を書きました。

ついでなので、xlsxファイルをGREPするものも作ったので、ここにメモしておきます。

サンプル

package main

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

    "github.com/devlights/goxcel"
)

type (
    Args struct {
        dir     string
        text    string
        onlyHit bool
        verbose bool
        debug   bool
    }
)

var (
    args Args
)

var (
    appLog = log.New(os.Stdout, "", 0)
)

func init() {
    flag.StringVar(&args.dir, "dir", ".", "directory path")
    flag.StringVar(&args.text, "text", "", "search text")
    flag.BoolVar(&args.onlyHit, "only-hit", true, "show ONLY HIT")
    flag.BoolVar(&args.verbose, "verbose", false, "verbose mode")
    flag.BoolVar(&args.debug, "debug", false, "debug mode")
}

func abs(p string) string {
    abs, err := filepath.Abs(p)
    if err != nil {
        log.Panic(err)
    }

    return abs
}

func main() {
    log.SetFlags(0)
    flag.Parse()

    if args.text == "" {
        flag.PrintDefaults()
        os.Exit(1)
    }

    if args.dir == "" {
        args.dir = "."
    }

    if err := run(); err != nil {
        log.Fatal(err)
    }
}

func run() error {
    quit := goxcel.MustInitGoxcel()
    defer quit()

    excel, release := goxcel.MustNewGoxcel()
    defer release()

    excel.MustSilent(false)

    wbs := excel.MustWorkbooks()

    rootDir := abs(args.dir)
    err := filepath.WalkDir(rootDir, func(path string, d fs.DirEntry, err error) error {
        if err != nil {
            return err
        }

        if d.IsDir() {
            return nil
        }

        if strings.Contains(filepath.Base(path), "~$") {
            return nil
        }

        if !strings.HasSuffix(path, ".xlsx") {
            return nil
        }

        absPath := abs(path)
        wb, wsRelease, err := wbs.Open(absPath)
        if err != nil {
            return err
        }
        defer wsRelease()

        if args.debug {
            appLog.Printf("Document Open: %s", absPath)
        }

        sheets, err := wb.WorkSheets()
        if err != nil {
            return err
        }

        relPath, _ := filepath.Rel(rootDir, absPath)
        _, err = sheets.Walk(func(ws *goxcel.Worksheet, index int) error {
            rng, err := ws.UsedRange()
            if err != nil {
                return err
            }

            startCell, err := rng.Cells(1, 1)
            if err != nil {
                return err
            }

            foundRange, found, err := rng.Find(args.text, startCell)
            if err != nil {
                return err
            }

            name, _ := ws.Name()
            if !found {
                if !args.onlyHit {
                    appLog.Printf("%s %q: No HIT", relPath, name)
                }

                return nil
            }

            if args.verbose {
                col, _ := foundRange.Column()
                row, _ := foundRange.Row()
                value, _ := foundRange.Value()

                appLog.Printf("%s %q (%d,%d): %q", relPath, name, row, col, value)
            } else {
                appLog.Printf("%s %q: HIT", relPath, name)
                return nil
            }

            startCell, _ = foundRange.Cells(1, 1)
            for i := 0; found; i++ {
                after, _ := foundRange.Cells(1, 1)

                foundRange, found, err = rng.FindNext(after)
                if err != nil {
                    return err
                }

                if !found {
                    break
                }

                if args.debug {
                    printCellPos(startCell, after)
                }

                if i > 0 && sameCell(startCell, after) {
                    break
                }

                if args.verbose {
                    col, _ := foundRange.Column()
                    row, _ := foundRange.Row()
                    value, _ := foundRange.Value()

                    appLog.Printf("%s %q (%d,%d): %q", relPath, name, row, col, value)
                }
            }

            return nil
        })

        if err != nil {
            return err
        }

        return nil
    })

    if err != nil {
        return err
    }

    return nil
}

func sameCell(c1, c2 *goxcel.Cell) bool {
    col1, _ := c1.Column()
    row1, _ := c1.Row()
    col2, _ := c2.Column()
    row2, _ := c2.Row()

    return col1 == col2 && row1 == row2
}

func printCellPos(startCell, after *goxcel.Cell) {
    col1, _ := startCell.Column()
    row1, _ := startCell.Row()
    col2, _ := after.Column()
    row2, _ := after.Row()

    appLog.Printf("FindNext: startCell=(%d,%d)\tafter=(%d,%d)", row1, col1, row2, col2)
}

以下のようにして利用します。

$ go build -o grep-xlsx.exe .
$ ./grep-xlsx.exe -dir /path/to/documents -text "データベースファイルサイズ" -verbose

https://github.com/devlights/goxcel/tree/main/examples/grep_xlsx

参考情報

GitHub - devlights/goxcel: Goxcel is a library to operate Excel using go-ole library.

Goのおすすめ書籍


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

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