Excelのセルを正規表現で検索したい場面が稀にあります。Excel本体ではワイルドカード検索しかできず、正規表現を使いたい場合には、VBAの力を借りる必要があります。そこで、Go言語を利用して、Excelのファイル検索をするツールを作ってみましょう。

  • 正規表現でExcelを検索するツールを作ろう

    正規表現でExcelを検索するツールを作ろう

Go言語でExcelファイルを読み書きする方法

Go言語にはExcelファイルを読み書きする、excelizeというライブラリがあります。このライブラリの使い方は、本連載の14回目でも紹介していますので参考にしてください。とは言え、その記事を書いた時点から4年も経過していますし、バージョンもずいぶん変わっています。

そこで、最新版v2.8.1の使い方を確認してみます。

まずは、プロジェクトを作成して、パッケージ「excelize」をインストールしましょう。ターミナル(WindowsならPowerShell、macOSならターミナル.app)を起動して、以下のコマンドを実行しましょう。

# プロジェクトを作成
mkdir re_excel
cd re_excel
go mod init re_excel
# excelizeをインストール
go get github.com/xuri/excelize/v2

続いて、簡単なプログラムで、使い方を確認しましょう。以下のプログラムを「main.go」という名前で保存しましょう。こちらhttps://gist.github.com/kujirahand/22f4eee3d61a5988dd1ba04c794dfcb7にもアップしています。

package main
import (
    "fmt"
    "log"
    "github.com/xuri/excelize/v2"
)
func main() {
    writeFile("test.xlsx")
    readFile("test.xlsx")
}
// ファイルに書き込みを行う関数 --- (*1)
func writeFile(fileName string) {
    // 新しいExcelファイルを作成
    f := excelize.NewFile()
    // シート名を指定してセルにデータを書き込む
    sheetName := "Sheet1"
    cell := "A1"
    value := "洞察力があればすぐに怒ることはない。"
    if err := f.SetCellValue(sheetName, cell, value); err != nil {
        log.Fatalf("書き込み失敗: %v", err)
    }
    // ファイル保存
    if err := f.SaveAs(fileName); err != nil {
        log.Fatalf("ファイルの保存に失敗: %v", err)
    }
    log.Printf("ファイルを保存しました: %s", fileName)
}
// ファイルを読み込む関数 --- (*2)
func readFile(filename string) {
    // 既存のExcelファイルを開く
    f, err := excelize.OpenFile(filename)
    if err != nil {
        log.Fatalf("ファイルを開くことができませんでした: %v", err)
    }
    defer f.Close()
    // A1セルの値を取得する
    sheetName := "Sheet1"
    cell := "A1"
    value, err := f.GetCellValue(sheetName, cell)
    if err != nil {
        log.Fatalf("セルの値を取得できませんでした: %v", err)
    }
    // 取得したセルの値を表示
    fmt.Printf("%sの値: %s\n", cell, value)
}

このプログラムを実行するには、ターミナルで下記のコマンドを実行します。

go run main.go

プログラムを実行すると「test.xlsx」というExcelファイルを生成します。そして、作成したExcelファイルのセルA1にある値を読み取ります。

  • Excelの読み書きを行うプログラムの実行例

    Excelの読み書きを行うプログラムの実行例

プログラムを確認してみましょう。(*1)ではExcelファイルを書き込む関数writeFileを定義します。ここでは、Sheet1のセルA1に格言を書き込みます。そして、SaveAsメソッドでファイルに保存します。

続いて(*2)の関数readFileでは、OpenFileメソッドでファイルを読み込み、Sheet1のセルA1の値を読み取り、値をコンソールに出力します。

このように、Excelを操作するパッケージ「excelize」の操作はとても簡単です。

複数ファイルを正規表現検索するツールを作ろう

それでは、コマンドラインから正規表現で複数ファイルを検索するツールを作ってみましょう。以下がカレントディレクトリにあるファイルにあるExcelファイルの全部のセルを正規表現検索するプログラムです。こちら(https://gist.github.com/kujirahand/d333bb6d5caec66c7753c52601476626)にも同じプログラムをアップしています。下記のソースコードを、先ほど作成したプロジェクトの「main.go」に上書きしてください。

package main
import (
    "fmt"
    "os"
    "regexp"
    "strings"
    "github.com/xuri/excelize/v2"
)
func main() {
    // コマンドライン引数を取得 --- (*1)
    args := os.Args
    if len(args) < 2 {
        fmt.Println("正規表現を指定してください")
        return
    }
    // 正規表現をコンパイル --- (*2)
    pattern := args[1]
    re, err := regexp.Compile(pattern)
    if err != nil {
        fmt.Printf("正規表現のコンパイルに失敗: %v", err)
        return
    }
    // カレントディレクトリのファイル一覧を取得 --- (*3)
    files, err := os.ReadDir(".")
    if err != nil {
        fmt.Printf("ディレクトリの読み込みに失敗: %v", err)
    }
    // ファイル一覧を調査
    for _, file := range files {
        fname := file.Name()
        if strings.HasSuffix(fname, ".xlsx") {
            searchFile(file.Name(), re)
        }
    }
}
func searchFile(filename string, re *regexp.Regexp) {
    // ファイルを検索 --- (*4)
    fmt.Printf("%s:\n", filename)
    // ファイルを開く
    f, err := excelize.OpenFile(filename)
    if err != nil {
        fmt.Printf("ファイルの読み込みに失敗: %v", err)
        return
    }
    // シート一覧を取得 --- (*5)
    sheets := f.GetSheetList()
    for _, sheet := range sheets {
        // シートの行を全部取得 --- (*6)
        rows, err := f.GetRows(sheet)
        if err != nil {
            continue
        }
        // 取得した行を調査 --- (*7)
        for rowIndex, row := range rows {
            matchCell := ""
            disp := "|"
            // セルを一つずつ調査 --- (*8)
            for colIndex, cell := range row {
                // cellの内容を正規表現で検索 --- (*9)
                if !re.Match([]byte(cell)) {
                    disp += fmt.Sprintf("%s|", cell)
                    continue // 見つからなかった時
                }
                disp += fmt.Sprintf("[%s]|", cell)
                cellName, _ := excelize.CoordinatesToCellName(colIndex+1, rowIndex+1)
                matchCell = cellName
            }
            if matchCell != "" {
                fmt.Printf("- %s.%s: %s\n", sheet, matchCell, disp)
            }
        }
    }
}

プログラムをコンパイルするには、ターミナルで以下のコマンドを実行します。

go build .

すると「re_excel」または「re_excel.exe」という実行ファイルが作成されます。それで、この実行ファイルをExcelの入ったフォルダにコピーして、ターミナルで次のようなコマンドを実行します。

# Windowsの場合
.\re_excel "正規表現”
# macOSの場合
./re_excel "正規表現"

なお、今回、サンプルとして、英単語辞書をExcel形式に変換した「543.xlsx」を用意しました。こちら(https://n3s.nadesi.com/image.php?f=543.xlsx)からダウンロードできます。このファイルは、パブリックドメインの英和辞書ejdic-handをExcel形式に変換したものです。以下は、macOSで試した場合の例です。Windowsの場合は「./」を「.\」に置換して試してください。

例えば、「app」からはじまり「zer」で終わる英単語を調べるには以下のコマンドを実行します。

./re_excel "^app.+zer$"

また、「ri」からはじまり2文字後空いて「r」で終わる単語を検索したい場合は、以下のようなコマンドを入力します。

./re_excel "^ri..r$"

実際に実行してみると、次のように表示されます。

  • 正規表現を指定して合致する英単語を表示

    正規表現を指定して合致する英単語を表示

英単語数が45000以上あり、全てのシートの全てのセルを検索するため、検索には少し時間がかかりますが、しっかり正規表現のパターンに沿ったデータ行を表示します。

プログラムを確認してみましょう。(*1)ではコマンドライン引数を取得します。(*2)では正規表現をコンパイルします。

(*3)ではカレントディレクトリにあるファイル一覧を取得してExcelファイルを一つずつ開いて内容を取得する(*4)の関数searchFileを実行します。

(*4)ではExcelファイルを開いて、(*5)ではシートの一覧を取得します。(*6)では各シートから全行を取得します。(*7)では各行、(*8)各セルをくまなく調べていきます。(*9)では正規表現を利用して検索します。

まとめ

以上、今回はGo言語でExcelファイルにあるデータを正規表現で検索するツールを作ってみました。ここでは、カレントディレクトリにあるファイルを検索するツールを作りましたが、指定のフォルダを調べるようオプションを追加したり、サブディレクトリを検索するように改良したりしてみると良いでしょう。いろいろな改良が考えられますので、お気に入りのコマンドラインツールを作ってみてください。

自由型プログラマー。くじらはんどにて、プログラミングの楽しさを伝える活動をしている。代表作に、日本語プログラミング言語「なでしこ」 、テキスト音楽「サクラ」など。2001年オンラインソフト大賞入賞、2004年度未踏ユース スーパークリエータ認定、2010年 OSS貢献者章受賞。技術書も多く執筆している。直近では、「シゴトがはかどる Python自動処理の教科書(マイナビ出版)」「すぐに使える!業務で実践できる! PythonによるAI・機械学習・深層学習アプリのつくり方 TensorFlow2対応(ソシム)」「マンガでざっくり学ぶPython(マイナビ出版)」など。