ソースコードから理解する技術-UnderSourceCode

手を動かす(プログラムを組む)ことで技術を理解するブログ

Golangのx.text.transformが便利だった

タイトルにある様にGolangのx.text.transformパッケージについてです。
このパッケージを使うと、readerやwriter内のデータを変換することができます。
良くある用途としては、readerで読み込んだ文字列の一部を変換して出力する、などがあると思います。

このパッケージを知った切っ掛けは以下のSlideShareです。
オススメの標準・準標準パッケージ20選

SlideShareで言及されていますが、パッケージのドキュメントは以下となり、Exampleを見れば使い方は分かるかと思います。
https://godoc.org/golang.org/x/text/transform#Transformer
https://godoc.org/github.com/tenntenn/text/transform#example-ReplaceTable

transformパッケージ自体については上記のリンクを見れば分かるのですが
自分が理解するために書いたサンプルソースもメモ代わりに上げておきます。

Golangで2つのgzファイルを連結してみる - ソースコードから理解する技術-UnderSourceCode
以前書いた上記のソースを改修したもので、2つの圧縮ファイル(中身はjson)を読み込み、値を変換して、圧縮ファイルに出力するものです。

サンプルソース

「sample1.json.gz」「sample2.json.gz」というgzファイルをあらかじめ用意しておき、実行すると中身が連結されて「result.json.gz」というファイルに出力されます。

sample1、sample2のjsonの中身は以下のような形式で、「abc」「fgh」をそれぞれ大文字に変換して出力しています。

sample1

{
    "data":"abcde"
}

sample2

{
    "data":"fghij"
}

メインの処理は以下のようになります。

package main

import (
	"compress/gzip"
	"fmt"
	"io"
	"log"
	"os"

	. "github.com/tenntenn/text/transform"
	"golang.org/x/text/transform"
)

func write(br1 io.Reader, br2 io.Reader) error {
	writeFile, err := os.OpenFile("./result.json.gz", os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		return err
	}
	defer writeFile.Close()

	zw := gzip.NewWriter(writeFile)
	defer zw.Close()

	_, err = io.Copy(zw, br1)
	if err != nil {
		return err
	}

	_, err = zw.Write([]byte("\n"))
	if err != nil {
		return err
	}

	_, err = io.Copy(zw, br2)
	if err != nil {
		return err
	}

	return nil
}

func doTransform(zr *gzip.Reader, old, new []byte) io.Reader {
	t := ReplaceByteTable{
		old, new,
	}
	return transform.NewReader(zr, ReplaceAll(t))
}

func main() {
	readFile1, err := os.Open("./sample1.json.gz")
	if err != nil {
		log.Fatal(err)
	}
	defer readFile1.Close()

	zr1, err := gzip.NewReader(readFile1)
	if err != nil {
		log.Fatal(err)
	}
	defer zr1.Close()

	readFile2, err := os.Open("./sample2.json.gz")
	if err != nil {
		log.Fatal(err)
	}
	defer readFile2.Close()

	zr2, err := gzip.NewReader(readFile2)
	if err != nil {
		log.Fatal(err)
	}
	defer zr2.Close()

	tr1 := doTransform(zr1, []byte(`abc`), []byte(`ABC`))
	tr2 := doTransform(zr2, []byte(`fgh`), []byte(`FGH`))

	err = write(tr1, tr2)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("-----finish-----")
}

transformパッケージを使っているのはdoTransform()メソッドになります。
.gzファイルを読み込んだreaderを受け取り、変換するルールをReplaceByteTableに定義し、新たに別のreaderを生成して返却しています。

返却したreaderはwrite()に渡し、そのままresult.json.gzに出力しています。

このような感じにreaderの中身を変換して出力できることが確認できました。