いろいろ備忘録日記

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

Goメモ-330 (新しいKEN_ALL (utf_all.csv) で遊ぶ)

関連記事

devlights.hatenablog.com

概要

上の関連記事であるとおり、皆さんおなじみのKEN_ALLさんが生まれ変わったので、少し遊んでみました。

KEN_ALLさんは、行数も10万行とかを超えてて、且つ、日本語が含まれているので、ファイル操作を覚えるのに個人的にはちょうど良いと思っています。

試してみる

package main

import (
    "bufio"
    "encoding/csv"
    "fmt"
    "io"
    "log"
    "os"
    "regexp"
)

const (
    KEN_ALL = "utf_all.csv"
)

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

func main() {
    if len(os.Args) <= 1 {
        fmt.Fprintln(os.Stderr, "./kenall 検索文字列")
        os.Exit(-1)
    }

    re, err := regexp.Compile(os.Args[1])
    if err != nil {
        panic(err)
    }

    if err := run(re); err != nil {
        panic(err)
    }
}

func run(re *regexp.Regexp) error {
    var (
        file *os.File
        err  error
    )

    file, err = os.Open(KEN_ALL)
    if err != nil {
        return fmt.Errorf("err: os.Open (%w)", err)
    }
    defer file.Close()

    var (
        bufReader = bufio.NewReader(file)
        csvReader = csv.NewReader(bufReader)
        recCh     = make(chan []string)
        addrCh    = make(chan string)
        resultCh  = make(chan string)
        errCh     = make(chan error, 1)
    )

    go read(csvReader, recCh, errCh)
    go concat(recCh, addrCh, errCh)
    go find(re, addrCh, resultCh, errCh)

LOOP:
    for {
        select {
        case v, ok := <-resultCh:
            if !ok {
                break LOOP
            }
            appLog.Println(v)
        case e := <-errCh:
            return e
        }
    }

    return nil
}

func read(in *csv.Reader, out chan<- []string, errCh chan<- error) {
    defer close(out)
    for {
        record, err := in.Read()
        if err == io.EOF {
            break
        }

        if err != nil {
            errCh <- fmt.Errorf("err: csv.Read (%w)", err)
            return
        }

        out <- record
    }
}

func concat(in <-chan []string, out chan<- string, errCh chan<- error) {
    defer close(out)
    for v := range in {
        out <- fmt.Sprintf("%s%s%s", v[6], v[7], v[8])
    }
}

func find(re *regexp.Regexp, in <-chan string, out chan<- string, errCh chan<- error) {
    defer close(out)
    for v := range in {
        if re.FindString(v) != "" {
            out <- v
        }
    }
}

タスクファイルは以下。

# https://taskfile.dev

version: '3'

tasks:
  clean:
    cmds:
      - rm -f utf_all.csv
      - go clean
  download:
    preconditions:
      - (! test -f utf_all.csv)
    cmds:
      - wget https://www.post.japanpost.jp/zipcode/utf_all.csv
      - file -i utf_all.csv
      - wc -l utf_all.csv
      - ls -lh utf_all.csv | awk '{print $5}'
    ignore_error: true
  run:
    cmds:
      - go build
      - ./kenall "^東京.*銀座"
      - cmd: echo ======================================
        silent: true
      - ./kenall "^[^(東京)].*銀座"
  run-with-time:
    cmds:
      - go build
      - time ./kenall "^東京.*銀座"
      - cmd: echo ======================================
        silent: true
      - time ./kenall "^[^(東京)].*銀座"

実行すると以下のようになります。

$ task download
task: [download] wget https://www.post.japanpost.jp/zipcode/utf_all.csv
--2023-07-16 17:46:56--  https://www.post.japanpost.jp/zipcode/utf_all.csv
Resolving www.post.japanpost.jp (www.post.japanpost.jp)... 43.253.212.17
Connecting to www.post.japanpost.jp (www.post.japanpost.jp)|43.253.212.17|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 18305986 (17M) [text/csv]
Saving to: ‘utf_all.csv’

utf_all.csv                      100%[========================================================>]  17.46M  1.89MB/s    in 10s     

2023-07-16 17:47:07 (1.70 MB/s) - ‘utf_all.csv’ saved [18305986/18305986]

task: [download] file -i utf_all.csv
utf_all.csv: text/csv; charset=utf-8
task: [download] wc -l utf_all.csv
124270 utf_all.csv
task: [download] ls -lh utf_all.csv | awk '{print $5}'
18M


$ task run
task: [run] go build
task: [run] ./kenall "^東京.*銀座"
東京都中央区銀座
======================================
task: [run] ./kenall "^[^(東京)].*銀座"
北海道夕張郡長沼町銀座
栃木県鹿沼市銀座
埼玉県熊谷市銀座
埼玉県本庄市銀座
富山県富山市新庄銀座
長野県岡谷市銀座
長野県岡谷市東銀座
長野県飯田市銀座
静岡県静岡市清水区銀座
静岡県熱海市銀座町
静岡県伊東市銀座元町
愛知県半田市銀座本町
愛知県刈谷市銀座
滋賀県彦根市銀座町
山口県周南市銀座
山口県周南市みなみ銀座
徳島県徳島市銀座
福岡県北九州市戸畑区銀座

銀座って名前はあっちこっちにあるんだなーって思ったりしてました。

上のコードは、ついでなのでリポジトリ作ってアップしておきました。よければご参考まで。

github.com

参考情報

www.post.japanpost.jp

Goのおすすめ書籍

Go言語による並行処理

Go言語による並行処理

Amazon


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

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