Skip to content
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

Kadai2 yasu #34

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
83 changes: 83 additions & 0 deletions kadai2/yasu/READWRITER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
## io.Writerとio.Readerについて

### io.Writer
#### どのように使われている?
io.Writerはfmtパッケージに置いて、FrintlnやFrintfなどに使われており、次のようになっています。

```print.go
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
p := newPrinter()
p.doPrintln(a)
n, err = w.Write(p.buf)
p.free()
return
}
```

ここでは受け取ったinterface{}の配列をinterfaceの持つ情報の文字列に変換し、その文字列をbyte型のスライスにしたものをw(io.Writer)に書き込んでいます。

では、このio.Writerが何者かと言うと
Printlnの実装を見てみると

```print.go
func Println(a ...interface{}) (n int, err error) {
return Fprintln(os.Stdout, a...)
}
```

os.Stdout、つまり標準出力が渡されています。
os.StdoutにはWriteメソッドが存在しておりWriteを発火させる事によって標準出力に渡されたbyte配列を書き込んでいるのです。
もちろん、Fprintlnにはos.Fileなどを渡すこともでき、io.Writerは書き込む先を抽象化するために使われてると言えます。

### io.Reader
#### どのように使われている?

fmtパッケージのScanlnを例にとって考えてみると 

```scan.go
func Fscan(r io.Reader, a ...interface{}) (n int, err error) {
s, old := newScanState(r, true, false)
n, err = s.doScan(a)
s.free(old)
return
}
```

io.Readerはここで使われています。
こちらでのReaderの使い方はFprintlnとほぼ一緒で

```scan.go
func Scan(a ...interface{}) (n int, err error) {
return Fscan(os.Stdin, a...)
}
```

```scan.go
func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) {
s = ssFree.Get().(*ss)
if rs, ok := r.(io.RuneScanner); ok {
s.rs = rs
} else {
s.rs = &readRune{reader: r, peekRune: -1}
}
s.nlIsSpace = nlIsSpace
s.nlIsEnd = nlIsEnd
s.atEOF = false
s.limit = hugeWid
s.argLimit = hugeWid
s.maxWid = hugeWid
s.validSave = true
s.count = 0
return
}

```

標準入力から受け取った文字列をssという構造体に書き込んでいます。

こちらもos.Stdinやos.Fileなどの構造体を抽象化するために用いられています。


### 利点
利点としては抽象化する事により実際に受け取っている値に依存する事なく関数を実装できる事だと思います。
抽象化する事により受け取った具象系の変更の影響を受けないので変更にも強いです。
69 changes: 69 additions & 0 deletions kadai2/yasu/converter/converter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
Converter
Converterパッケージは画像の変換プログラムを提供します。
*/

package converter

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

// ConvertImg は画像ファイルを指定したformatに変換します。
func ConvertImg(file *os.File, directory string, afterFormat string) error {
img, _, err := image.Decode(file)
if err != nil {
return err
}
switch afterFormat {
case "png":
out, err := os.Create(file.Name() + ".png")
Copy link

Choose a reason for hiding this comment

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

これだと foo.jpg というファイルの入力の場合に foo.jpg.png というファイル名になりませんか?
それは意図したものでしょうか?

if err != nil {
return err
}
convertToPng(&img, out)
case "jpeg":
out, err := os.Create(file.Name() + ".jpg")
if err != nil {
return err
}
convertToJpeg(&img, out)
case "gif":
out, err := os.Create(file.Name() + ".gif")
if err != nil {
return err
}
convertToGif(&img, out)
}
return nil
}

func convertToPng(img *image.Image, out *os.File) error {
err := png.Encode(out, *img)
if err != nil {
return err
}
return nil
}

func convertToGif(img *image.Image, out *os.File) error {
opts := gif.Options{NumColors: 256}
err := gif.Encode(out, *img, &opts)
if err != nil {
return err
}
return nil
}

func convertToJpeg(img *image.Image, out *os.File) error {
opts := jpeg.Options{Quality: 100}
err := jpeg.Encode(out, *img, &opts)
if err != nil {
return err
}
return nil
}
64 changes: 64 additions & 0 deletions kadai2/yasu/converter/converter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package converter

import (
"image"
"os"
"testing"
)

func TestConvertImg(t *testing.T) {
file, err := os.Open("../sample_image/3d.jpg")
if err != nil {
t.Fatalf("failed test %#v", err)
}
directory := "sample_image"
err = ConvertImg(file, directory, "png")
if err != nil {
t.Fatalf("failed test %#v", err)
}
}

func TestconvertToGif(t *testing.T) {
file, err := os.Open("../sample_image/3d.jpg")
if err != nil {
t.Fatalf("failed test %#v", err)
}
img, _, err := image.Decode(file)
if err != nil {
t.Fatalf("failed test %#v", err)
}
out, err := os.Create(file.Name() + ".gif")
if err := convertToGif(&img, out); err != nil {
t.Fatalf("failed test %#v", err)
}
}

func TestconvertToPng(t *testing.T) {
file, err := os.Open("../sample_image/3d.jpg")
if err != nil {
t.Fatalf("failed test %#v", err)
}
img, _, err := image.Decode(file)
if err != nil {
t.Fatalf("failed test %#v", err)
}
out, err := os.Create(file.Name() + ".gif")
if err := convertToPng(&img, out); err != nil {
t.Fatalf("failed test %#v", err)
}
}

func TestconvertToJpg(t *testing.T) {
file, err := os.Open("../sample_image/3d.jpg")
if err != nil {
t.Fatalf("failed test %#v", err)
}
img, _, err := image.Decode(file)
if err != nil {
t.Fatalf("failed test %#v", err)
}
out, err := os.Create(file.Name() + ".gif")
if err := convertToJpeg(&img, out); err != nil {
t.Fatalf("failed test %#v", err)
}
}
67 changes: 67 additions & 0 deletions kadai2/yasu/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
Overview
このコードはGopher道場1回目の課題のソースコードです。
画像を変換するプログラムです。
Usage
go buildをし、バイナリを実行してください。
./yasu some_directory でsome_directoryに指定されたdirectory内の
全ての画像ファイルをpngファイルに変換します。
Options
バイナリを実行する際にオプションを指定する事が可能です。
to オプションを指定すると変換先の画像フォーマットを変更できます。
次のように指定するとgifフォーマットへと変換します。
./yasu -to gif some_directory
*/

package main

import (
"dojo4/kadai1/yasu/converter"
"flag"
"io/ioutil"
"os"
"sync"
)

// オプション保持用変数
var (
//before_format string
afterFormat string // 変換先format
)

var wg = sync.WaitGroup{}

func main() {
bindFlags()
directory := os.Args[len(os.Args)-1]
if err = analyzeFiles(directory); err != nil {
log.Println(err)
}
}

func bindFlags() {
//flag.StringVar(&before_format, "from", "jpeg", "File format")
flag.StringVar(&afterFormat, "to", "png", "Converted file format")
flag.Parse()
}

func analyzeFiles(directory string) error {
files, err := ioutil.ReadDir(directory) // Get all file information in directory
if err != nil {
return err
}
for _, fileInfo := range files {
file, err := os.Open(directory + "/" + fileInfo.Name()) // Read file
Copy link

Choose a reason for hiding this comment

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

できればfilepath.Join使ってほしいです。
あと対象のディレクトリのなかに更にディレクトリがあった場合、それも変換しようとしますが大丈夫でしょうか?

if err != nil {
return err
}
wg.Add(1)
go func(file *os.File) {
if err := converter.ConvertImg(file, directory, afterFormat); err != nil {
return err
}
wg.Done()
}(file)
}
wg.Wait()
}
Binary file added kadai2/yasu/sample_image/3d.jpg
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 kadai2/yasu/sample_image/hdri.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.