Golang Cafe #12 まとめ compressパッケージ
2014/01/12に開催された「Golang Cafe #12」についてのまとめです。
今回はcompressパッケージ以下をみていきました。
サンプルコードは+TakashiYokoyama氏が準備してくれています。
基本的な処理の流れは前回と同じなのでまとめが簡単になっているかもしれません。
lzwパッケージとzlibパッケージについては、サンプルコードもほぼ同じになる(パッケージ修飾が異なる程度)ので説明は省略されました。
bzip2パッケージ
bzip2パッケージはbzip2形式で圧縮されたファイルを解凍することができます。圧縮することはできません。
func main() { var file *os.File var err error if file, err = os.Open("files/golang.txt.bz2"); err != nil { log.Fatalln(err) } defer file.Close() // bzip2はReaderしかない。 reader := bzip2.NewReader(file) io.Copy(os.Stdout, reader) }
flateパッケージ
flateパッケージはflate形式での圧縮および解凍を行うためのパッケージです。
恥ずかしながらflate形式というのは知らなかったのですが、画像の圧縮等に使用されるもののようです。
圧縮
func main() { var file *os.File var err error var writer *flate.Writer var body []byte if file, err = os.Create("output/sample.tar.flate"); err != nil { log.Fatalln(err) } defer file.Close() // flateはレベルを指定する。flate.BestSpeed or flate.BestCompression or DefaultCompression if writer, err = flate.NewWriter(file, flate.BestCompression); err != nil { log.Fatalln(err) } defer writer.Close() var filepaths = []string { "files/b0044482_1413812.jpg", "files/dart_flight_school.png", "files/golang.txt", } tw := tar.NewWriter(writer) defer tw.Close() for _, filepath := range filepaths { if body, err = ioutil.ReadFile(filepath); err != nil { log.Fatalln(err) } if body != nil { hdr := &tar.Header { Name: path.Base(filepath), Size: int64(len(body)), } if err := tw.WriteHeader(hdr); err != nil { println(err) } if _, err := tw.Write(body); err != nil { println(err) } } } }
Windowsユーザには馴染みが少ないかもしれませんが、このサンプルではtarでまとめたものをflateで圧縮するという2段階の処理を行っていので多少複雑になっています。
- flate.NewWriterメソッドでWriterを作成します。引数を2つとり、1つ目はio.WriterなのでファイルでなくてもWrite(p []byte) (n int, err error)なメソッドを実装しているものなら何でも構いません。2つ目は圧縮率を指定することができ、-1から9までを指定できるようです。この値はconstantsで定義されています。
- 直接Writeメソッドで書きだすこともできますが、このサンプルでは作成したWriterをtarパッケージのWriterに渡して書き込んでいます。tarパッケージのWriterに渡せる理由と書き出し方法は前回まとめたので省略します。
単純に次へ次へとWriterを渡しているだけなのですが最初「ん?」となるかも知れないので補足的に他の言語で書いてみると、
C#
using (FileStream fs = new FileStream(出力先)) using (Flate flate = new Flate(fs)) using (Tar tar = new Tar(flate)) { tar.Write(); }
try (FileOutputStream fos = new FileOutputStream(出力先); Flate flate = new Flate(fos); Tar tar = new Tar(flate)) { tar.write(); }
このような形でしょうか。データ→tar→flate→出力先ファイルという流れです。
※Flate、Tarクラスは説明するために適当に書いているクラスです、実際に存在するかどうかは分かりません。
解凍
func main() { var file *os.File var err error if file, err = os.Open("output/sample.tar.flate"); err != nil { log.Fatalln(err) } defer file.Close() tr := tar.NewReader(flate.NewReader(file)) var header *tar.Header for { header, err = tr.Next() if err == io.EOF { // ファイルの最後 break } if err != nil { log.Fatalln(err) } buf := new(bytes.Buffer) if _, err = io.Copy(buf, tr); err != nil { log.Fatalln(err) } if err = ioutil.WriteFile("output/" + header.Name, buf.Bytes(), 0755); err != nil { log.Fatal(err) } } }
gzipパッケージ
gzipパッケージはgzip形式での圧縮および解凍を行うためのパッケージです。
圧縮
func main() { var file *os.File var err error var writer *gzip.Writer var body []byte if file, err = os.Create("output/sample.tar.gz"); err != nil { log.Fatalln(err) } defer file.Close() // gzip.NewWriter()なら、エラーを返さないので便利 if writer, err = gzip.NewWriterLevel(file, gzip.BestCompression); err != nil { log.Fatalln(err) } defer writer.Close() var filepaths = []string { "files/b0044482_1413812.jpg", "files/dart_flight_school.png", "files/golang.txt", } tw := tar.NewWriter(writer) defer tw.Close() for _, filepath := range filepaths { if body, err = ioutil.ReadFile(filepath); err != nil { log.Fatalln(err) } if body != nil { hdr := &tar.Header { Name: path.Base(filepath), Size: int64(len(body)), } if err := tw.WriteHeader(hdr); err != nil { println(err) } if _, err := tw.Write(body); err != nil { println(err) } } } }
このサンプルでは先ほどのflateのサンプルと同じようにtarでまとめたものをgzipで圧縮するという2段階の処理を行っています。
処理の手順についてはgzip.NewWriterLevelに変更になっている程度なので詳細な説明は割愛します。
解凍
func main() { var file *os.File var err error var reader *gzip.Reader if file, err = os.Open("output/sample.tar.gz"); err != nil { log.Fatalln(err) } defer file.Close() if reader, err = gzip.NewReader(file); err != nil { log.Fatalln(err) } defer reader.Close() tr := tar.NewReader(reader) var header *tar.Header for { header, err = tr.Next() if err == io.EOF { // ファイルの最後 break } if err != nil { log.Fatalln(err) } buf := new(bytes.Buffer) if _, err = io.Copy(buf, tr); err != nil { log.Fatalln(err) } if err = ioutil.WriteFile("output/" + header.Name, buf.Bytes(), 0755); err != nil { log.Fatal(err) } } }
解凍についても先ほどのflateのサンプルとほぼ同じで、gzip.NewReaderに変更になっているだけなので説明は省略します。
その他
今回のどのパッケージに関してもだと思うのですが、NewReader系メソッド(関数)、NewWriter系メソッドで、2つ目の戻り値でerrorが返ってくるものとそうでないのもがありました。これについては疑問が残ったので(できれば)別エントリでまとめておきたいと思います。
また今回もType assertionsについては議論がありました。これについてはどこかのタイミングで一度しっかりやろうということになりました。
次回
次回は、containerパッケージをみていくそうです。