io.Writer 解析

简介

io.Writer 跟 io.Reader 一样,都是 Interface 类型,功能非常强大,在任何需要写入数据,处理数据流的地方,我们都应该尽可能使用这两个类型的对象。

io.Writer 的原型:

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

跟 io.Reader 类似,一个对象只要实现了 Write() 函数,这个对象就自动成为 Writer 类型。

常见 Writer 类型

(1)文件操作
使用 os.Create() 创建文件时,会返回一个 os.File 对象,它是一个 struct,但是由于它实现了 Read() ,Write(),Closer() 等函数,因此它同时也是 Reader, Writer, Closer 等类型。

type File struct {
    *file // os specific
}

func (f *File) Write(b []byte) (n int, err error) {
    if err := f.checkValid("write"); err != nil {
        return 0, err
    }
    n, e := f.write(b)
    if n < 0 {
        n = 0
    }
    if n != len(b) {
        err = io.ErrShortWrite
    }

    epipecheck(f, e)

    if e != nil {
        err = &PathError{"write", f.name, e}
    }
    return n, err
}

(2)bytes.Buffer
在 Go 语言中,string 类型是 immutable 的,因此它没有对应的 Writer,也就是说不存在 strings.NewWriter(s) 这种函数。最好的替代方式就是使用 bytes.Buffer,因为它既是一个 Reader 也是一个 Writer,我们既可以往里面写也可以往外读。我们可以通过 buf.String() 得到 string 类型的数据,也可以通过 buf.Bytes() 拿到 []byte 类型的数据。

下面的例子展示了我们通过 bytes.NewBufferString(s) 函数,先在 buffer 中初始化一段 string,然后往里面 append 另外一段 string。

s := "Hello"
buf := bytes.NewBufferString(s)
s2 := "to be appended"
buf.WriteString(s2)   // 或者 fmt.Fprint(buf, s2)
fmt.Println("Final string:", buf.String())

(3)http.ResponseWriter
在使用 Go 语言进行 Web 开发时,http.ResponseWriter 是最基本的类型之一,它本身是一个 Interface 类,原型如下:

type ResponseWriter interface {
    Header() Header
    Write([]byte) (int, error)
    WriteHeader(int)
}

可见,它只申明了需要实现三个函数,由于其要求了 Writer() 函数,包含了 Writer 的要求,因此,任何是符合 ResponserWriter 的类型必然是 Writer 类型。

下面是一个http.ResponseWriter 最常见的使用场景和方法的:

一、直接调用 Write() 写入一串 []byte

func helloHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/plain") 
    w.Write([]byte("Hello World"))
    return
}

func main() {
    http.HandleFunc("/", helloHandler)
    http.ListenAndServe(":8080", nil)
}

这种方式跟创建文件时,直接往文件对象中写入 []byte 是一样的:

func main() {
    f, _:= os.Create("test.txt")
    if _, err = f.Write([]byte("Hello world")); err != nil {
            log.Fatal(err)
    }
    f.Close()
}

二、或者 使用 io.WriteStirng() 或者 fmt.Fprintf() 往 Writer 中写入 string

func helloHandler(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "Hello world!")
        fmt.Fprintf(w, "Hello, World!")
}
func main() {
    http.HandleFunc("/", helloHandler)
    http.ListenAndServe(":8080", nil)
}

(4) hash.Hash
hash 包中申明了 Hash 这个 interface 接口,作为所有 hash 函数的公共接口。它也是一个 Writer,原型如下:

type Hash interface {
    // Write (via the embedded io.Writer interface) adds more data to the running hash.
    // It never returns an error.
    io.Writer

    // Sum appends the current hash to b and returns the resulting slice.
    // It does not change the underlying hash state.
    Sum(b []byte) []byte

    // Reset resets the Hash to its initial state.
    Reset()

    // Size returns the number of bytes Sum will return.
    Size() int

    // BlockSize returns the hash's underlying block size.
    // The Write method must be able to accept any amount
    // of data, but it may operate more efficiently if all writes
    // are a multiple of the block size.
    BlockSize() int
}

使用方式

import (
    "crypto/sha1"
)

func main() {
    passwordHash := sha1.New()
    io.WriteString(passwordHash, combination) // 或者直接 passwordHash.Write(combination)
    fmt.Printf("Password Hash : %x \n", passwordHash.Sum(nil))
}

(5) bufio
可以通过 bufio.NewWriter(r) 函数来把原 io.Writer 对象封装成一个 bufio.Writer 对象,从而进行 buffered 读写。

package main

import (
    "bufio"
    "os"
)

func main() {
    f, _ := os.Create("file.txt")
    w := bufio.NewWriter(f) // Create a new writer.
    w.WriteString("ABC")  // Write a string to the file.
    w.Flush()
}

(6) gzip 压缩

package main

import (
    "bufio"
    "compress/gzip"
    "fmt"
    "io/ioutil"
    "os"
    "strings"
)

func main() {
    // Open file on disk.
    name := "file.txt"
    f, _ := os.Open("C:\\programs\\" + name)

    // Create a Reader and use ReadAll to get all the bytes from the file.
    reader := bufio.NewReader(f)
    content, _ := ioutil.ReadAll(reader)

    // Replace txt extension with gz extension.
    name = strings.Replace(name, ".txt", ".gz", -1)

    f, _ = os.Create("C:\\programs\\" + name)

    w := gzip.NewWriter(f)
    w.Write(content)
    w.Close()
}

推荐阅读更多精彩内容

  • Golang作为一门新的编程语言,它借鉴了现有语言的思想但拥有着不同寻常的特性,使得有效的Go程序在性质上不同于其...
    云时代的运维开发阅读 621评论 0 0
  • 原文:https://spino.tech/blog/important-go-interfaces/#golan...
    devabel阅读 1,103评论 1 4
  • 简介 io.Reader 是一个 Interface 类型,功能非常强大,在任何需要读的地方我们都尽量使用它。先来...
    juniway阅读 6,066评论 0 3
  • Go reflect 反射实例解析 ——教坏小朋友系列 0 FBI WARNING 对于本文内容,看懂即可,完全不...
    楊浥塵阅读 541评论 0 3
  • 嗨,大家好! 我的名字是Sergey Kamardin,我是Mail.Ru的工程师。 介绍 首先介绍我们的故事的上...
    肆虐的悲傷阅读 2,429评论 1 11
  • 久违的晴天,家长会。 家长大会开好到教室时,离放学已经没多少时间了。班主任说已经安排了三个家长分享经验。 放学铃声...
    飘雪儿5阅读 6,769评论 16 21
  • 今天感恩节哎,感谢一直在我身边的亲朋好友。感恩相遇!感恩不离不弃。 中午开了第一次的党会,身份的转变要...
    迷月闪星情阅读 9,728评论 0 10
  • 可爱进取,孤独成精。努力飞翔,天堂翱翔。战争美好,孤独进取。胆大飞翔,成就辉煌。努力进取,遥望,和谐家园。可爱游走...
    赵原野阅读 2,348评论 1 1
  • 在妖界我有个名头叫胡百晓,无论是何事,只要找到胡百晓即可有解决的办法。因为是只狐狸大家以讹传讹叫我“倾城百晓”,...
    猫九0110阅读 2,691评论 7 3