io.Reader、io.Writer


在〈从标准输入、输出认识 io〉中谈到了io.Readerio.Writer,在 Go 中,这两个接口抽象化了输入、输出,认识这两个接口分别定义的ReadWrite行为,是掌握 Go 中输入、输出的基础。

io.Reader定义的Read行为,可以在type Reader查看:

type Reader interface {
    Read(p []byte) (n int, err error)
}

对于调用者来说,Read会将数据读入p,并返回读入的字节数nn会是 0 到不大于len(p)的整数,如果n不是 0 但不足len(p),应该先处理已读取的字节,这时err可能不是nil(例如文件结尾,可能会返回io.EOF),无论如何,在这之后Readn会是 0 而err会是io.EOF

例如,若要读取一个文本文件,其中以 UTF-8 存储中文,可以如下:

package main

import (
    "fmt"
    "io"
    "os"
)

func printUTF8TC(r io.Reader) (err error) {
    var (
        buf = make([]byte, 3)
        n int
    )

    for err == nil {
        n, err = r.Read(buf)
        fmt.Print(string(buf[:n]))
    }
    if err == io.EOF {
        err = nil
    }
    return
}

func main() {
    fmt.Print("文件来源:")
    var filename string
    fmt.Scanf("%s", &filename)

    f, err := os.Open(filename)
    if err != nil {
        panic(err)
    }
    defer f.Close()

    printUTF8TC(f)
}

io.Writer定义的Write行为,可以在type Writer查看:

type Writer interface {
    Write(p []byte) (n int, err error)
}

Write会将p输出并返回实际输出的字节,n会是 0 到不大于len(p)的整数,如果n < len(p),那么err不会是nil

来写个Copy函数好了,可以将io.Reader的数据直接写到io.Writer

package main

import (
    "fmt"
    "io"
    "os"
)

func write(w io.Writer, buf []byte, n int) (err error) {
    nw, ew := w.Write(buf[:n])
    if ew != nil {
        return ew
    }
    if n != nw {
        return io.ErrShortWrite
    }
    return nil
}

func Copy(w io.Writer, r io.Reader) (err error) {
    buf := make([]byte, 32 * 1024)
    for {
        nr, er := r.Read(buf)
        if nr > 0 {
            err = write(w, buf, nr)
            if err != nil {
                return
            }
        }
        if er != nil {
            if er != io.EOF {
                err = er
            }
            return
        }
    }
}

func main() {
    fmt.Print("文件来源:")
    var filename string
    fmt.Scanf("%s", &filename)

    f, err := os.Open(filename)
    if err != nil {
        panic(err)
    }
    defer f.Close()

    Copy(os.Stdout, f)
}

在这个例子中,可以将指定的文件读入并显示在控制台中,这是因为os.Stdout具有io.Writer的行为。实际上,io.Copy就提供了这个功能:

package main

import (
    "fmt"
    "io"
    "os"
)

func main() {
    fmt.Print("文件来源:")
    var filename string
    fmt.Scanf("%s", &filename)

    f, err := os.Open(filename)
    if err != nil {
        panic(err)
    }
    defer f.Close()

    io.Copy(os.Stdout, f)
}




展开阅读全文