-
Notifications
You must be signed in to change notification settings - Fork 0
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
hokita / 課題1 #5
base: master
Are you sure you want to change the base?
hokita / 課題1 #5
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
# 課題 1 画像変換コマンドを作ろう | ||
|
||
## 課題内容 | ||
### 次の仕様を満たすコマンドを作って下さい | ||
|
||
- ディレクトリを指定する | ||
- 指定したディレクトリ以下の JPG ファイルを PNG に変換(デフォルト) | ||
- ディレクトリ以下は再帰的に処理する | ||
- 変換前と変換後の画像形式を指定できる(オプション) | ||
|
||
### 以下を満たすように開発してください | ||
|
||
- main パッケージと分離する | ||
- 自作パッケージと標準パッケージと準標準パッケージのみ使う | ||
- 準標準パッケージ:golang.org/x 以下のパッケージ | ||
- ユーザ定義型を作ってみる | ||
- GoDoc を生成してみる | ||
- Go Modules を使ってみる | ||
|
||
## 対応したこと | ||
- 画像を変換 | ||
- 現状はjpg, pngのみ | ||
- jpg, png以外はエラー表示 | ||
- 画像出力先は対象画像と同じディレクトリ | ||
- 指定したディレクトリが無いとエラーを表示 | ||
|
||
## 動作 | ||
```shell | ||
$ go build -o test_imgconv | ||
|
||
$ ./test_imgconv -h | ||
Usage of ./test_imgconv: | ||
-from string | ||
Conversion source extension. (default "jpg") | ||
-to string | ||
Conversion target extension. (default "png") | ||
|
||
# testdata内のすべてのjpgファイルをpngに変換する | ||
$ ./test_imgconv testdata | ||
Conversion finished! | ||
|
||
# testdata内のすべてのpngファイルをjpgに変換する | ||
$ ./test_imgconv -from png -to jpg testdata | ||
Conversion finished! | ||
|
||
# ディレクトリの指定が無い場合はエラー | ||
$ ./test_imgconv | ||
Please specify a directory. | ||
|
||
# 存在しないディレクトリの場合はエラー | ||
$ ./test_imgconv non_exist_dir | ||
Cannot find directory. | ||
|
||
# 対応していない拡張子の場合はエラー | ||
$ ./test_imgconv -from txt -to jpg testdata | ||
Selected extension is not supported. | ||
``` | ||
|
||
## 工夫したこと | ||
- png, jpg以外にも拡張子が増えそうなので、`image_type`というinterfaceを作ってみた。 | ||
- 拡張子の微妙な違い(jpg, jpeg, JPGなど)にも対応できるようにした。 | ||
|
||
## わからなかったこと、むずかしかったこと | ||
- go mod initで指定するmodule名に命名規則があるのか。 | ||
- 普段オブジェクト指向(その上動的型付け言語)で書いているので、それがgoらしいコードになっているのか不安。 | ||
- なんでもかんでも構造体メソッドにしたい願望がでてくる |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module imgconv | ||
|
||
go 1.14 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package imgconv | ||
|
||
import ( | ||
"image" | ||
"os" | ||
"path/filepath" | ||
) | ||
|
||
func converterFactory(from string, to string) (*Converter, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 型で十分伝わるので名前は |
||
fromImage, err := selectImage("." + from) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
toImage, err := selectImage("." + to) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &Converter{fromImage, toImage}, nil | ||
} | ||
|
||
type Converter struct { | ||
fromImage ImageType | ||
toImage ImageType | ||
} | ||
|
||
func (conv *Converter) Execute(path string) error { | ||
// ignore unrelated file | ||
if !conv.fromImage.IsMatchExt(filepath.Ext(path)) { | ||
return nil | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. これだと成功したのか失敗したのか無視されたのか分かりづらいので、エラーを返して、呼び出し元で判断してもらうのが良さそうですね。たとえば、 |
||
} | ||
|
||
// file open | ||
file, err := os.Open(path) | ||
defer file.Close() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. defer Closeはエラー処理のあと |
||
if err != nil { | ||
return err | ||
} | ||
|
||
// convert to image obj | ||
img, _, err := image.Decode(file) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// output file | ||
out, err := os.Create(conv.SwitchExt(path)) | ||
defer out.Close() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 書き込みの場合では |
||
if err != nil { | ||
return err | ||
} | ||
|
||
// output image | ||
conv.toImage.Encode(out, img) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. エンコードするのにエラーが起きることはない? |
||
return nil | ||
} | ||
|
||
func (conv *Converter) SwitchExt(path string) string { | ||
ext := filepath.Ext(path) | ||
toExt := conv.toImage.Extensions()[0] | ||
|
||
return path[:len(path)-len(ext)] + toExt | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package imgconv | ||
|
||
import ( | ||
"errors" | ||
"image" | ||
"io" | ||
) | ||
|
||
type ImageType interface { | ||
Encode(w io.Writer, m image.Image) error | ||
IsMatchExt(ext string) bool | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
Extensions() []string | ||
} | ||
|
||
func selectImage(ext string) (ImageType, error) { | ||
pngImage := PngImage{} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. フィールドの初期化を伴わない構造体リテラル(コンポジットリテラル)での初期化は不要です。 |
||
jpegImage := JpegImage{} | ||
|
||
if pngImage.IsMatchExt(ext) { | ||
return pngImage, nil | ||
} else if jpegImage.IsMatchExt(ext) { | ||
return jpegImage, nil | ||
} | ||
|
||
return nil, errors.New("Selected extension is not supported.") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package imgconv | ||
|
||
import ( | ||
"errors" | ||
"flag" | ||
"os" | ||
"path/filepath" | ||
) | ||
|
||
func Call() error { | ||
var ( | ||
from = flag.String("from", "jpg", "Conversion source extension.") | ||
to = flag.String("to", "png", "Conversion target extension.") | ||
) | ||
flag.Parse() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
dir := flag.Arg(0) | ||
|
||
if flag.Arg(0) == "" { | ||
return errors.New("Please specify a directory.") | ||
} | ||
|
||
if f, err := os.Stat(dir); os.IsNotExist(err) || !f.IsDir() { | ||
return errors.New("Cannot find directory.") | ||
} | ||
|
||
converter, err := converterFactory(*from, *to) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = filepath.Walk(dir, | ||
func(path string, info os.FileInfo, err error) error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 引数のエラーのエラー処理。 |
||
err = converter.Execute(path) | ||
return err | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package imgconv | ||
|
||
import ( | ||
"image" | ||
"image/jpeg" | ||
"io" | ||
) | ||
|
||
const QUALITY = 100 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 定数であってもキャメルケースで書きます。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. JPEGのQUALITYっていうのが分かるようにしたほうが良さそうですね。 |
||
|
||
type JpegImage struct{} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. JPEGで十分だと思います。Goでは略語はすべて大文字か全部小文字にします。 |
||
|
||
func (_ JpegImage) Encode(w io.Writer, m image.Image) error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. レシーバを参照しない場合は |
||
err := jpeg.Encode(w, m, &jpeg.Options{Quality: QUALITY}) | ||
return err | ||
} | ||
|
||
func (ji JpegImage) IsMatchExt(ext string) bool { | ||
for _, myExt := range ji.Extensions() { | ||
if ext == myExt { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func (_ JpegImage) Extensions() []string { | ||
return []string{".jpg", ".jpeg", ".JPG", ".JPEG"} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mapの方がいいのと、メソッドを呼び出すたびに生成する必要はないのでパッケージ変数にしても良さそうです There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 以下同様です。 |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package imgconv | ||
|
||
import ( | ||
"image" | ||
"image/png" | ||
"io" | ||
) | ||
|
||
type PngImage struct{} | ||
|
||
func (_ PngImage) Encode(w io.Writer, m image.Image) error { | ||
err := png.Encode(w, m) | ||
return err | ||
} | ||
|
||
func (pi PngImage) IsMatchExt(ext string) bool { | ||
for _, myExt := range pi.Extensions() { | ||
if ext == myExt { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func (_ PngImage) Extensions() []string { | ||
return []string{".png", ".PNG"} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"imgconv/imgconv" | ||
"os" | ||
) | ||
|
||
const ( | ||
ExitCodeOk int = iota | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
ExitCodeError | ||
) | ||
|
||
func main() { | ||
os.Exit(run()) | ||
} | ||
|
||
func run() int { | ||
err := imgconv.Call() | ||
|
||
if err != nil { | ||
fmt.Fprintln(os.Stderr, err) | ||
return ExitCodeError | ||
} | ||
|
||
fmt.Println("Conversion finished!") | ||
return ExitCodeOk | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
from, to string
と書けます