Skip to content

Kadai2 simady #16

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions kadai1/simady/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## 概要
指定ディレクトリ配下にある画像ファイルを、再帰的に走査して別の形式のファイルに変換します。png, jpeg, gifに対応しています。

## 実行方法

```
go run main.go -in TARGET_PATH -src SOURCE_EXTENSION -dst DESTINATION_EXTENSION

-in 走査したいディレクトリを指定します。
-src 変換元のファイル形式を指定します。
-dst 変換後のファイル形式を指定します。
```
77 changes: 77 additions & 0 deletions kadai1/simady/converter/converter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package converter

import (
"image"
"log"
"os"
)

// IConverter コンバーターインターフェース
type IConverter interface {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Goでは先頭にIとつける習慣はない。

convert(file *os.File, img image.Image) error
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

型名がexportされているのにメソッド名がexportされていない理由は?

validate() bool
}

type converter struct {
src, dst string
}

var (
convertibleExts = []string{"jpeg", "png", "gif"}
)

// Validate フィールドのバリデーション処理を行う.
func (c converter) validate() bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

構造体の場合、特に理由がない場合はレシーバにはポインタを使う。
https://qiita.com/knsh14/items/8b73b31822c109d4c497#receiver-type

return validate(c.src) && validate(c.dst)
}

func validate(ext string) bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なぜメソッドでやらずに関数で行う?

for _, e := range convertibleExts {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mapを使えば関数不要そう。
https://play.golang.org/p/11bEbuN1BLW

if ext == e {
return true
}
}
return false
}

// Convert 画像フォーマットの変換を行う.
func Convert(path string, c IConverter, fileName string) error {

src, err := os.Open(path)
if err != nil {
log.Fatalf("Faild to open file. err = %v\n", err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

log.Fatalは中でos.Exitを呼び出すので基本的にmain関数以外では使わない。
errorを返す。
main関数であってもox.Exitに渡す終了コードを指定することができないため、基本的には使わない。

}
defer src.Close()

var img image.Image
img, _, err = image.Decode(src)
if err != nil {
log.Fatalf("Faild to decode file. err = %v\n", err)
}

var dst *os.File
dst, err = os.Create(fileName)
if err != nil {
log.Fatalf("Faild to create file. err = %v\n", err)
}
defer dst.Close()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

書き込み用のファイルは閉じる際にエラー処理をする。


return c.convert(dst, img)
}

// GetConverter 拡張子に対応したコンバーターを取得する.
func GetConverter(src, dst string) IConverter {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GoではGetという単語はあんまり使わない。

c := converter{src: src, dst: dst}
c.validate()
switch dst {
case "png":
return pngConverter{c}
case "jpeg":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jpgも扱えるようにしたほうが良さそう。

return jpegConverter{c}
case "gif":
return gifConverter{c}
default:
log.Fatalf("Unsupported extension. dst = %v\n", dst)
}
return nil
}
67 changes: 67 additions & 0 deletions kadai1/simady/converter/converter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package converter

import (
"testing"
)

func TestGetConverter(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

テーブル駆動テストをする

var c IConverter
c = GetConverter("jpeg", "png")
if _, ok := c.(pngConverter); !ok {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

せっかくインタフェースにしているのに、テストが具体的な実装に依存しているのはあまりよくない。

t.Fatal("c is not pngConverter.")
}

c = GetConverter("png", "gif")
if _, ok := c.(gifConverter); !ok {
t.Fatal("c is not gifConverter.")
}

c = GetConverter("gif", "jpeg")
if _, ok := c.(jpegConverter); !ok {
t.Fatal("c is not jpegConverter.")
}
}

func TestValidate(t *testing.T) {
var src, dst string

// 正常系
src, dst = "jpeg", "png"
if !(converter{src: src, dst: dst}).validate() {
t.Fatalf("Expect src=%v dst=%v to be supported.\n", src, dst)
}

src, dst = "png", "gif"
if !(converter{src: src, dst: dst}).validate() {
t.Fatalf("Expect src=%v dst=%v to be supported.\n", src, dst)
}

src, dst = "gif", "jpeg"
if !(converter{src: src, dst: dst}).validate() {
t.Fatalf("Expect src=%v dst=%v to be supported.\n", src, dst)
}

// 異常系
src, dst = "png", "webp"
if (converter{src: src, dst: dst}).validate() {
t.Fatalf("src=%v dst=%v not supported.\n", src, dst)
}
}

func TestConvert(t *testing.T) {
var path, fileName string
path, fileName = "../src/gopher.jpeg", "../dst/test/test.png"
if err := Convert(path, pngConverter{}, fileName); err != nil {
t.Fatal("Failed to convert.")
}

path, fileName = "../src/gopher.png", "../dst/test/test.gif"
if err := Convert(path, gifConverter{}, fileName); err != nil {
t.Fatal("Failed to convert.")
}

path, fileName = "../src/gopher.gif", "../dst/test/test.jpeg"
if err := Convert(path, jpegConverter{}, fileName); err != nil {
t.Fatal("Failed to convert.")
}
}
16 changes: 16 additions & 0 deletions kadai1/simady/converter/gif_conveter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package converter

import (
"image"
"image/gif"
"os"
)

// gifConverter Gif用コンバーター
type gifConverter struct {
converter
}

func (gc gifConverter) convert(file *os.File, img image.Image) error {
return gif.Encode(file, img, &gif.Options{NumColors: 256})
}
16 changes: 16 additions & 0 deletions kadai1/simady/converter/jpeg_converter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package converter

import (
"image"
"image/jpeg"
"os"
)

// jpegConverter Jpeg用コンバーター
type jpegConverter struct {
converter
}

func (jc jpegConverter) convert(file *os.File, img image.Image) error {
return jpeg.Encode(file, img, &jpeg.Options{Quality: 100})
}
16 changes: 16 additions & 0 deletions kadai1/simady/converter/png_converter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package converter

import (
"image"
"image/png"
"os"
)

// pngConverter Png用コンバーター
type pngConverter struct {
converter
}

func (pc pngConverter) convert(file *os.File, img image.Image) error {
return png.Encode(file, img)
}
Binary file added kadai1/simady/dst/gopher.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added kadai1/simady/dst/gopher.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added kadai1/simady/dst/gopher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added kadai1/simady/dst/test/test.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added kadai1/simady/dst/test/test.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added kadai1/simady/dst/test/test.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions kadai1/simady/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module imgconv

go 1.12
37 changes: 37 additions & 0 deletions kadai1/simady/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package main

import (
"flag"
"log"
"os"
"path/filepath"
"strings"

"imgconv/converter"
)

var (
in, out, src, dst string
)

func init() {
flag.StringVar(&in, "in", "", "target directory.")
flag.StringVar(&src, "src", "jpeg", "source extension. [jpeg,png]")
flag.StringVar(&dst, "dst", "png", "destination extension. [jpeg,png]")
}

func main() {
flag.Parse()
c := converter.GetConverter(src, dst)
err := filepath.Walk(in, func(path string, info os.FileInfo, err error) error {
ext := filepath.Ext(path)
if ext != "."+src {
return nil
}
out = "dst/" + strings.ReplaceAll(filepath.Base(path), ext, "."+dst)
return converter.Convert(path, c, out)
})
if err != nil {
log.Fatalf("Error occurred in convert process. err = %v\n", err)
}
}
Binary file added kadai1/simady/src/gopher.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added kadai1/simady/src/gopher.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added kadai1/simady/src/gopher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
75 changes: 75 additions & 0 deletions kadai2/simady/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
## 概要
指定ディレクトリ配下にある画像ファイルを、再帰的に走査して別の形式のファイルに変換します。
png, jpeg, gifに対応しています。

## 実行方法

```
imgconv -in INPUT_PATH -out OUTPUT_PATH -src SOURCE_EXTENSION -dst DESTINATION_EXTENSION

-in 走査したいディレクトリを指定します。
-out 出力先のディレクトリを指定します。
-src 変換元のファイル形式を指定します。
-dst 変換後のファイル形式を指定します。
```

## テストカバレッジ
```
$ go test -cover imgconv/...
ok imgconv 0.434s coverage: 88.2% of statements
ok imgconv/converter 0.430s coverage: 100.0% of statements
```

### io.ReaderとioWriterについて
```
type Reader interface {
Read(p []byte) (n int, err error)
}
```
byte型のスライスを引数に取り、pに値を読み込む。
読み込んだバイト数とエラーを返す。

```
type Writer interface {
Write(p []byte) (n int, err error)
}
```
byte型のスライスを引数に取り、pから値を書き込む。
書き込んだバイト数とエラーを返す。

- 標準パッケージでどのように使われているか
- tarやzip等のアーカイブ形式
- bzip2やlzw等の圧縮形式
- ECDSAやtls等の暗号化形式
- base64や16進数等のフォーマット
- gifやpng等の画像形式
- 標準入出力や標準エラー出力
- http通信でのリクレス等
- MIMEマルチパートやQuoted-printable等の方式
- ファイル
- 文字列やバイトバッファ
- ログ

上記のように様々な箇所での読み書きに使用されていた。
また、以下のようなerrorの戻り値を利用するような使い方も見られた。
```
type errorReader struct {
error
}
func (r errorReader) Read(p []byte) (n int, err error) {
return 0, r.error
}
```

```
type eofReader struct{}
func (eofReader) Read([]byte) (int, error) {
return 0, EOF
}
```

- io.Readerとio.Writerがあることでどういう利点があるのか具体例を挙げて考えてみる
- io.Readerやio.Writerを実装したインスタンスをインターフェースを介して利用することで、使用する側は実体が何かを意識せずに扱うことができる。
- シンプルなインターフェースなので実装が容易。何に対して読み書きを行うかは定義されていないので、入出力先に応じて適宜実装すれば良い。
- テストやデバッグ時に、実際の処理とは別の処理に置き換えるといった差し替えが容易に行える。
- ioパッケージのpipeやio/ioutilパッケージで定義された既存の便利メソッドを利用できる。
57 changes: 57 additions & 0 deletions kadai2/simady/converter/converter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package converter

import (
"fmt"
"image"
"os"
)

// IConverter コンバーターインターフェース
type IConverter interface {
Convert(path string, fileName string) error
}

type converter struct {
Encoder
}

type Encoder interface {
encode(file *os.File, img image.Image) error
}

// NewConverter 拡張子に対応したコンバーターを生成する.
func NewConverter(dst string) (IConverter, error) {
switch dst {
case "png":
return converter{&pngEncoder{}}, nil
case "jpg":
return converter{&jpgEncoder{}}, nil
case "gif":
return converter{&gifEncoder{}}, nil
}
return nil, ImgconvError{Message: fmt.Sprintf("Unsupported extension. dst = %v\n", dst)}
}

// Convert 画像フォーマットの変換を行う.
func (c converter) Convert(path string, fileName string) error {
src, err := os.Open(path)
if err != nil {
return err
}
defer src.Close()

var img image.Image
img, _, err = image.Decode(src)
if err != nil {
return err
}

var dst *os.File
dst, err = os.Create(fileName)
if err != nil {
return err
}
defer dst.Close()

return c.encode(dst, img)
}
Loading