漫游Go语言(六)之Error、Reader和Image练习
Error
Go
使用error
类型的值来表示错误,这是一个内建的类似Stringer
的接口。1
2
3type error interface {
Error() string
}
类似Stringer
,fmt
包也会寻找error
接口来打印值。
函数经常以error
作为返回类型,调用函数的代码应该通过检测error
是否为nil
并处理处理。一般来说error == nil
说明无错误,而非nil则代表failure.
error练习
实现一个开方函数,除返回结果外,还返回error
,当被操作数是负数是返回一个非nil
的error
,其Error()
方法返回“cannot Sqrt negative number: -2”信息。
创建一个MyFloat
的类type MyFloat float64
,然后实现func (mf MyFloat ) Error() string
使之成为一个error
。
这是我的答案1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20package main
import ( "fmt"
"math")
type MyFloat float64
func (mf MyFloat) Error() string{
return fmt.Sprintf("cannot Sqrt negative number:%v.\n",float64(mf))
}
func Sqrt(f float64) (float64,error){
if f < 0 {
return 0, MyFloat(f)
}
return math.Sqrt(f),nil
}
func main(){
fmt.Println(Sqrt(4.3))
fmt.Println(Sqrt(-5.2))
}
运行:1
22.073644135332772 <nil>
0 cannot Sqrt negative number:-5.2.
这里需要注意的一点是,在Error()
方法里,当用Sprintf
返回string
时,一定要将其强制转换为基本类型float64
,否则就会进入死循环直到stack overflow
.这是因为Error
方法的Sprintf
语句中打印MyFloat
类型又会调用Error
,从而进入死循环。
Readers
io包中定义了io.Reader接口,代表数据stream的读取端。Go的标准库包含这些接口的多种实现,包括文件、网络、压缩器、加密器等。
io.reader接口有一个Read方法:1
func (T) Read(b []byte) (n int, err error)
Read
向指定的slice
中填充数据并返回读取的字节数和一个error
,当文件结束时它返回一个io.EOF
的error
.
下例中使用NewReader
方法创建了一个io.Reader
并使用其Read
方法一次读入8字节。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21package main
import (
"fmt"
"io"
"strings"
)
func main() {
r := strings.NewReader("Hello, Reader!")
b := make([]byte, 8)
for {
n, err := r.Read(b)
fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
fmt.Printf("b[:n] = %q\n", b[:n])
if err == io.EOF {
break
}
}
}
Reader练习
实现一个Reader
类型,从中能读出无穷的'A'
字符。
这个应该是比较简单地,直接给出我的实现。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19package main
import "golang.org/x/tour/reader"
type MyReader struct{}
// TODO: Add a Read([]byte) (int, error) method to MyReader.
func (mr MyReader) Read(b []byte) (int ,error){
len := 0
for i := range b{
b[i] = 'A'
len ++
}
return len ,nil
}
func main() {
reader.Validate(MyReader{})
}
练习:rot13Reader
一种常见的模式是用一个io.Reader
包装另一个io.Reader
,以对stream
实现修改。
例如gzip.NewReader
函数接收一个io.Reader
(压缩数据的stream),返回一个*gzip.Reader
,它也是一个io.Reader
(一个已解压数据的stream
).
题目:实现一个rot13Reader
,它从一个io.Reader
中读取数据并实现rot13
加密算法(所有字母顺移13位。
这个题目有一点工作量,以下是我的实现:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47package main
import (
"io"
"os"
"strings"
)
type rot13Reader struct {
r io.Reader
}
func isAlphabet(c byte) bool {
return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z'
}
func rot13(c byte) byte {
if c >= 'a' && c <= 'z' {
if c += 13; c > 'z' {
c = c%'z' + 'a' - 1
}
}
if c >= 'A' && c <= 'Z' {
if c += 13; c > 'Z' {
c = c%'Z' + 'A' - 1
}
}
return c
}
func (r rot13Reader) Read(b []byte) (int, error) {
n, e := r.r.Read(b)
for i := 0; i < n; i++ {
if isAlphabet(b[i]) {
b[i] = rot13(b[i])
}
if e == io.EOF {
break
}
}
return n, e
}
func main() {
s := strings.NewReader("Lbh penpxrq gur pbqr!")
r := rot13Reader{s}
io.Copy(os.Stdout, &r)
}
运行:1
You cracked the code!
Image接口
image包定义了Image接口1
2
3
4
5
6
7package image
type Image interface {
ColorModel() color.Model
Bounds() Rectangle
At(x, y int) color.Color
}
其中Bounds
方法返回的Rectangle
类型实质是image.Rectangle
,因为它是在包内定义的。
详细信息https://golang.org/pkg/image/#Imagecolor.Color
和color.Model
都是接口,但是我们将暂时略过它们而使用预实现的color.RGBA
和color.GRGBAModel
。这些接口和类型在image/color package中定义1
2
3
4
5
6
7
8
9
10
11
12package main
import (
"fmt"
"image"
)
func main() {
m := image.NewRGBA(image.Rect(0, 0, 100, 100))
fmt.Println(m.Bounds())
fmt.Println(m.At(0, 0).RGBA())
}
运行:
1 | (0,0)-(100,100) |
image练习
还记得之前的生成图像练习吗?不过这次我们要实现一个image.Image
而不是一个slice
.
定义你自己的Image
类型,实现必要的方法,并调用pic.ShowImage()
。1
2
3
4
5
6
7
8
9
10
11type Image interface {
// ColorModel returns the Image's color model.
ColorModel() color.Model
// Bounds returns the domain for which At can return non-zero color.
// The bounds do not necessarily contain the point (0, 0).
Bounds() Rectangle
// At returns the color of the pixel at (x, y).
// At(Bounds().Min.X, Bounds().Min.Y) returns the upper-left pixel of the grid.
// At(Bounds().Max.X-1, Bounds().Max.Y-1) returns the lower-right one.
At(x, y int) color.Color
}
Bounds
应该返回一个image.Rectangle
,类似image.Rect(0,0,w,h)
.ColorModel
应该返回color.RGBAModel
。
事实以上教程可能比较老了,当我查阅了新的Image
相关api
文档之后,类似Rect
这样的type
都已经换了形式。以下是我的实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27package main
import "golang.org/x/tour/pic"
import "image/color"
import "image"
type Image struct {
w, h int
colr uint8
}
func (i Image) ColorModel() color.Model {
return color.RGBAModel
}
func (i Image) Bounds() image.Rectangle {
return image.Rectangle{image.Point{i.w, i.h}, image.Point{110, 110}}
}
func (i Image) At(x, y int) color.Color {
return color.RGBA{i.colr+uint8(x), i.colr+uint8(y), 255, 255}
}
func main() {
m := Image{0,0,50}
pic.ShowImage(m)
}
打印的图片是