Skip to content

Commit 1119c7d

Browse files
author
sho-tatsuno
committed
add kadai-1 & README.md
1 parent 149e820 commit 1119c7d

File tree

13 files changed

+471
-0
lines changed

13 files changed

+471
-0
lines changed

kadai2/sh-tatsuno/README.md

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# 概要
2+
* 画像変換ツールgophotoの改良
3+
* io.Readerとio.Writerについて(下記)
4+
5+
# 画像変換ツールgophoto
6+
使い方としては下記のようにdirectoryを-d, -iで入力形式, -oで出力形式を指定する
7+
-nで最大変換数を指定する. 現在の対応は.jpeg, .jpg, .png, .gif
8+
```shell
9+
$ gophoto -d dir -i .png -o .jpeg -n 10
10+
```
11+
12+
13+
# io.Readerとio.Writerの調査
14+
* 標準パッケージでどのように使われているか
15+
* io.Readerとio.Writerがあることでどういう利点があるのか
16+
17+
## 概要
18+
* io.Writer: 出力の抽象化
19+
* io.Reader: 入力の抽象化
20+
21+
```golang
22+
type Writer interface {
23+
Write(p []byte)(n int, err error)
24+
}
25+
```
26+
27+
```golang
28+
type Reader interface {
29+
Read(p []byte) (n int, err error)
30+
}
31+
```
32+
33+
ReadとWriteをそれぞれ実装したものが全てReader, Writerのインターフェースとして扱われる
34+
35+
## 標準パッケージ
36+
### Write/Readが実装されているもの
37+
* syscall
38+
* システムコールに利用する
39+
* file
40+
* ファイルの読み書き
41+
* os.Stdout
42+
* 標準出力
43+
* bytes.Buffer
44+
* バイトへの読み書き
45+
* strings.Builder
46+
* Stringとのやりとり
47+
* net.IPConn
48+
* ネットワークへの読み書き
49+
50+
## io.Writer
51+
io.Writerという入出力に共通する処理を仕様として満たすインターフェース型を定義することでバイト列bを書き込んでバイト数n及びエラーeを出力する処理を通常のファイルに限定せずに汎用的に行うことができる
52+
53+
例えば、
54+
io.MultiWriterのように複数のio.Writerを受け取り書き込み内容を同時に書き込む関数を使うことで、様々な形式の書き込みに対して抽象的に処理できる
55+
56+
## io.Reader
57+
読み込むデータ型がRead関数を実装していれば予め用意した[]byte形式のバッファに読み込むことができる。ただし、バッファ管理をしつつ、毎回格納するバッファのサイズを用意してそこに突っ込むのは面倒なため、ioutil.RealAllのような補助関数を利用することでより利便性高く利用できる
58+
59+
## その他
60+
io.WriteString()のような関数がある。これらはbyteのsliceを使わずに直接stringに書き込むため標準のWriteメソッドではなくこちらを使った方が効率がいい
61+
62+
## 類似するioインターフェース
63+
* io.Closer
64+
* Closeメソッドを持ち、使用したファイルを閉じる
65+
* io.Seeker
66+
* 読み書きの位置を移動する
67+
* io.ReadAt
68+
* 対象オブジェクトがランダムアクセスできるときに特定の位置に自由にアクセスできる
69+
70+
## 参考
71+
Goならわかるシステムプログラミング, 渋川よしき, LambdaNote, 2017
72+
[How to use the io.Writer interface · YourBasic Go](https://yourbasic.org/golang/io-writer-interface-explained/)

kadai2/sh-tatsuno/gophoto/conv/img.go

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package conv
2+
3+
import (
4+
"image"
5+
"image/gif" // import gif
6+
"image/jpeg" // import jpeg
7+
"image/png" // import png
8+
"os"
9+
"path/filepath"
10+
11+
"golang.org/x/xerrors"
12+
)
13+
14+
// Img image struct
15+
type Img struct {
16+
Path string
17+
Data image.Image
18+
}
19+
20+
// NewImg generate Img struct
21+
func NewImg(path string) (*Img, error) {
22+
file, err := os.Open(path)
23+
defer file.Close()
24+
if err != nil {
25+
return nil, err
26+
}
27+
img, _, err := image.Decode(file)
28+
if err != nil {
29+
return nil, err
30+
}
31+
return &Img{
32+
Path: path,
33+
Data: img,
34+
}, nil
35+
}
36+
37+
// Save save img for input format if format satisfies ".jpeg", ".jpg", ".gif", ".png"
38+
func (i *Img) Save(path string) error {
39+
ext := filepath.Ext(path)
40+
var err error
41+
err = func(ext string) error {
42+
for _, suffix := range []string{".jpeg", ".jpg", ".gif", ".png"} {
43+
if ext == suffix {
44+
return nil
45+
}
46+
}
47+
return xerrors.New("invalid extension")
48+
}(ext)
49+
if err != nil {
50+
return err
51+
}
52+
53+
// if file exist, do nothing
54+
if _, err := os.Stat(path); !os.IsNotExist(err) {
55+
return nil
56+
}
57+
58+
dst, err := os.Create(path)
59+
if err != nil {
60+
return err
61+
}
62+
defer dst.Close()
63+
64+
switch ext {
65+
case ".jpeg", ".jpg":
66+
err = jpeg.Encode(dst, i.Data, nil)
67+
case ".gif":
68+
err = gif.Encode(dst, i.Data, nil)
69+
case ".png":
70+
err = png.Encode(dst, i.Data)
71+
default:
72+
err = xerrors.New("error in main method")
73+
}
74+
return err
75+
}
76+
77+
// AddExt add extension
78+
func (i *Img) AddExt(ext string) string {
79+
return i.Path + ext
80+
}
81+
82+
// Convert convert image
83+
func (i *Img) Convert(ext string) error {
84+
85+
// check path
86+
prevPath := i.Path
87+
if filepath.Ext(prevPath) == ext {
88+
return nil
89+
}
90+
91+
// save file
92+
newPath := prevPath[:len(prevPath)-len(filepath.Ext(prevPath))] + ext
93+
94+
// if file exist, do nothing
95+
if _, err := os.Stat(newPath); !os.IsNotExist(err) {
96+
return nil
97+
}
98+
99+
if err := i.Save(newPath); err != nil {
100+
return err
101+
}
102+
103+
// remove old file
104+
if err := os.Remove(prevPath); err != nil {
105+
return err
106+
}
107+
i.Path = newPath
108+
return nil
109+
}
+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package conv
2+
3+
import (
4+
"image/jpeg"
5+
"image/png"
6+
"os"
7+
"testing"
8+
)
9+
10+
func Test_Img(t *testing.T) {
11+
t.Run("OK: .png -> .jpeg", func(t *testing.T) {
12+
13+
// ### Given ###
14+
img, err := NewImg("./lena.jpeg")
15+
if err != nil {
16+
t.Fatalf("Cannot load file. err: %v", err)
17+
}
18+
19+
// ### When ###
20+
path := "./lena-png.jpeg"
21+
if err = img.Save(path); err != nil {
22+
t.Fatalf("Cannot save file. err: %v", err)
23+
}
24+
25+
// ### Then ###
26+
file, err := os.Open(path)
27+
if err != nil {
28+
t.Fatalf("Cannot open saved file. err: %v", err)
29+
}
30+
if _, err = jpeg.Decode(file); err != nil {
31+
t.Fatalf("Cannot decode saved file. err: %v", err)
32+
}
33+
if err := os.Remove(path); err != nil {
34+
t.Fatalf("Cannot remove saved file. err: %v", err)
35+
}
36+
37+
})
38+
39+
t.Run("OK: .jpeg -> .png", func(t *testing.T) {
40+
41+
// ### Given ###
42+
img, err := NewImg("./lena.png")
43+
if err != nil {
44+
t.Fatalf("Cannot load file. err: %v", err)
45+
}
46+
47+
// ### When ###
48+
path := "./lena-jpeg.png"
49+
if err = img.Save(path); err != nil {
50+
t.Fatalf("Cannot save file. err: %v", err)
51+
}
52+
53+
// ### Then ###
54+
file, err := os.Open(path)
55+
if err != nil {
56+
t.Fatalf("Cannot open saved file. err: %v", err)
57+
}
58+
if _, err = png.Decode(file); err != nil {
59+
t.Fatalf("Cannot decode saved file. err: %v", err)
60+
}
61+
if err := os.Remove(path); err != nil {
62+
t.Fatalf("Cannot remove saved file. err: %v", err)
63+
}
64+
65+
})
66+
67+
t.Run("NG: cannot openfile", func(t *testing.T) {
68+
69+
// ### Given ###
70+
_, err := NewImg("./noexist.png")
71+
if err == nil {
72+
t.Fatal("Load no exist file.")
73+
}
74+
})
75+
76+
t.Run("NG: invalid extension", func(t *testing.T) {
77+
78+
// ### Given ###
79+
_, err := NewImg("./img.go")
80+
if err == nil {
81+
t.Fatal("Load invalid file.")
82+
}
83+
})
84+
85+
}
8.02 KB
Loading
116 KB
Loading
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package dir
2+
3+
import (
4+
"io/ioutil"
5+
"path/filepath"
6+
)
7+
8+
// Lookup lookup directory to find image files
9+
func Lookup(dir string, ext string, pathList []string) ([]string, error) {
10+
11+
fileInfos, err := ioutil.ReadDir(dir)
12+
if err != nil {
13+
return nil, err
14+
}
15+
16+
for _, fileInfo := range fileInfos {
17+
18+
// get file info
19+
path := filepath.Join(dir, fileInfo.Name())
20+
21+
// check if the path is about directory
22+
if fileInfo.IsDir() {
23+
pathList, err = Lookup(path, ext, pathList)
24+
if err != nil {
25+
return nil, err
26+
}
27+
}
28+
29+
// check if the suffix is equal to the input format
30+
if filepath.Ext(path) == ext {
31+
pathList = append(pathList, path)
32+
}
33+
}
34+
35+
return pathList, nil
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package dir
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func Test_Lookup(t *testing.T) {
9+
10+
t.Run("OK: .jpg", func(t *testing.T) {
11+
var actual []string
12+
// ### Given ###
13+
actual, err := Lookup("./test", ".jpg", actual)
14+
if err != nil {
15+
t.Fatalf("Failed test. err: %v", err)
16+
}
17+
18+
expected := []string{"test/test1.jpg", "test/test3/test4.jpg", "test/test3/test5.jpg"}
19+
20+
// ### Then ###
21+
if !reflect.DeepEqual(actual, expected) {
22+
t.Fatalf("Failed test. expected: %v,\n but actual: %v", expected, actual)
23+
}
24+
})
25+
26+
t.Run("OK: .png", func(t *testing.T) {
27+
var actual []string
28+
// ### Given ###
29+
actual, err := Lookup("./test", ".png", actual)
30+
if err != nil {
31+
t.Fatalf("Failed test. err: %v", err)
32+
}
33+
34+
expected := []string{"test/test2.png", "test/test3/test6.png"}
35+
36+
// ### Then ###
37+
if !reflect.DeepEqual(actual, expected) {
38+
t.Fatalf("Failed test. expected: %v,\n but actual: %v", expected, actual)
39+
}
40+
})
41+
42+
t.Run("OK: .gif(empty)", func(t *testing.T) {
43+
var actual []string
44+
// ### Given ###
45+
actual, err := Lookup("./test", ".gif", actual)
46+
if err != nil {
47+
t.Fatalf("Failed test. err: %v", err)
48+
}
49+
50+
var expected []string
51+
52+
// ### Then ###
53+
if !reflect.DeepEqual(actual, expected) {
54+
t.Fatalf("Failed test. expected: %v,\n but actual: %v", expected, actual)
55+
}
56+
})
57+
58+
t.Run("NG: not found directory", func(t *testing.T) {
59+
var actual []string
60+
// ### Given ###
61+
_, err := Lookup("./test2", ".jpg", actual)
62+
if err == nil {
63+
t.Fatalf("should be error.")
64+
}
65+
66+
expected := "open ./test2: no such file or directory"
67+
68+
// ### Then ###
69+
if err.Error() != expected {
70+
t.Fatalf("Failed test. expected: %s,\n but actual: %s", expected, err.Error())
71+
}
72+
})
73+
}

kadai2/sh-tatsuno/gophoto/dir/test/test1.jpg

Loading

kadai2/sh-tatsuno/gophoto/dir/test/test2.png

Loading

kadai2/sh-tatsuno/gophoto/dir/test/test3/test4.jpg

Loading

kadai2/sh-tatsuno/gophoto/dir/test/test3/test5.jpg

Loading

kadai2/sh-tatsuno/gophoto/dir/test/test3/test6.png

Loading

0 commit comments

Comments
 (0)