Quantcast
Channel: プログラミング
Viewing all articles
Browse latest Browse all 8031

Goメモ-471 (複数のdocxファイルをGREPする) - いろいろ備忘録日記

$
0
0

関連記事

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

概要

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

特定のフォルダの下にdocxファイルが何個もあって、探すのが大変なときって結構あったりします。

テキストファイルであればgrepすれば良いのですが、wordのgrepってあまり聞きません。

なので、ちょこっと作ってみました。サクッと探すだけなら個人的にこれで充分なので、結構重宝しています。

サンプル

package main

import (
    "flag""fmt""log""os""path/filepath""strings""github.com/devlights/gord/constants""github.com/devlights/gord"
)

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")
    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 main() {
    flag.Parse()

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

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

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

func abs(p string) string {
    v, _ := filepath.Abs(p)
    return v
}

func genErr(procName string, err error) error {
    return fmt.Errorf("%s failed: %w", procName, err)
}

func run() error {
    quitFn, _ := gord.InitGord()
    defer quitFn()

    word, wordRelease, _ := gord.NewGord()
    defer wordRelease()

    _ = word.Silent(false)

    docs, err := word.Documents()
    if err != nil {
        return genErr("word.Documents()", err)
    }

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

        if d.IsDir() {
            returnnil
        }

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

        if !strings.HasSuffix(path, ".docx") {
            returnnil
        }

        absPath := abs(path)
        doc, docRelease, err := docs.Open(absPath)
        if err != nil {
            return err
        }
        defer docRelease()

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

        allRange, err := doc.AllRange()
        if err != nil {
            return genErr("doc.AllRange()", err)
        }

        find, err := allRange.Find()
        if err != nil {
            return genErr("range.Find()", err)
        }

        if err := find.Forward(true); err != nil {
            return genErr("find.Forward()", err)
        }

        if err := find.Wrap(constants.WdFindWrapFindStop); err != nil {
            return genErr("find.Wrap()", err)
        }

        if err := find.Format(false); err != nil {
            return genErr("find.Format()", err)
        }

        if err := find.MatchCase(false); err != nil {
            return genErr("find.MatchCase()", err)
        }

        // ワイルドカードを有効にすると MatchCase の設定が無効となるため// サーチテキストに * が含まれている場合のみ有効となるようにする.//// > A wildcard search is always case-sensitive.// > You'll notice the same in the interface: if you tick the "Use wildcards" check box,// > the "Match case" and the "Find whole words only" check boxes will be disabled;//// REF: http://www.vbaexpress.com/forum/showthread.php?41816-Match-Case-in-Find-does-not-work// REF: https://answers.microsoft.com/en-us/msoffice/forum/all/matchwildcardstrue-renders-matchcase-inoperative/c20685ff-99c8-4334-9e59-c5fb95ff617cif strings.Contains(args.text, "*") {
            if err := find.MatchWildcards(true); err != nil {
                return genErr("find.MatchWildcards()", err)
            }
        }

        if err := find.Text(args.text); err != nil {
            return genErr("find.Text()", err)
        }

        found, err := find.Execute()
        if err != nil {
            return genErr("find.Execute() [first time]", err)
        }

        relPath, _ := filepath.Rel(rootDir, absPath)
        if found {
            if args.verbose {
                foundRange := allRange

                for found {
                    text, _ := foundRange.Text()
                    page, _ := foundRange.PageNumber()
                    line, _ := foundRange.LineNo()

                    message := fmt.Sprintf("%s (%3d,%3d): %q", relPath, page, line, text)
                    appLog.Println(message)

                    found, err = find.Execute()
                    if err != nil {
                        return genErr("find.Execute() [after the second time]", err)
                    }

                    foundRange = allRange
                }
            } else {
                appLog.Printf("%s: HIT", relPath)
            }
        } else {
            if !args.onlyHit {
                appLog.Printf("%s: NO HIT", relPath)
            }
        }

        returnnil
    })

    if err != nil {
        return err
    }

    returnnil
}

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

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

gord/examples/grep_docx at main · devlights/gord · GitHub

参考情報

GitHub - devlights/gord: Gord is a library to operate MS Word using go-ole library.

Goのおすすめ書籍


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

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


Viewing all articles
Browse latest Browse all 8031

Trending Articles