以前的一篇有关终端读写的内容比较详细:
https://blog.51cto.com/steed/2315597服务器
这篇,先补充了两个从终端逐行扫描并处理的示例。而后再补充了有关两个写入操做的内容。ide
关于bufio包,使用它能够简便和高效地处理输入和输出。其中一个最有用的特性是称为扫描器(Scanner)的类型,它能够读取输入,以行或者单词为单位断开,这是处理以行为单位输入内容的最简单方式。
先声明一个 bufio.Scanner 类型的变量:函数
input := bufio.NewScanner(os.Stdin)
扫描器从程序的标准输入进行读取。每一次调用 inout.Scan() 读取下一行,而且将结尾的换行符去掉;经过调用 input.Text() 来获取读到的内容。Scan 方法在读到新行的时候返回true,在没有更多内容的时候返回flase。
下面的例子,从标准输入获取到内容后,转成全大写再打印出来。Windows下使用 Ctrl+Z 后回车能够退出:工具
package main import ( "bufio" "fmt" "os" "strings" ) func main() { input := bufio.NewScanner(os.Stdin) for input.Scan() { fmt.Println(strings.ToUpper(input.Text())) } }
上面的示例是每从标准输入获取到一行的内容,就进行处理。还有一种作法是,从标准输入获取所有的内容后(Ctrl+Z 后回车表示输入完成),最后再一次所有输出。下面的示例是从标准输入获取到所有内容,而后所有输出,每一行的内容之间插入一个空格。最后也会有一个空格,输出时把最后一个空格截断:性能
package main import ( "bufio" "bytes" "fmt" "os" ) func main() { input := bufio.NewScanner(os.Stdin) buff := bytes.NewBuffer(nil) for input.Scan() { buff.Write(input.Bytes()) // 忽略错误 buff.WriteByte(' ') } fmt.Printf("%q\n", buff.String()[:buff.Len()-1]) // 截掉最后一个空格 }
使用io.Copy函数读取响应的内容,好比直接复制内容到标准输出,这样就不须要把数据流装到缓冲区:code
n, err := io.Copy(os.Stdout, resp.Body)
还能够经过写入 ioutil.Discard 输出流进行丢弃,这样作应该是为了要有一个读取的过程:blog
n, err := io.Copy(ioutil.Discard, resp.Body)
这里要讲的是经过接口类型断言来查询特性,下面是一个标准库中使用的示例。而且,这个是向 io.Writer 写入字符串的推荐方法。
下面定义一个方法,往 io.Writer 接口接入字符串信息:接口
func writeMsg(w io.Writer, msg string) error { if _, err := w.Write([]byte(msg)); err != nil { return err } return nil }
由于 Write 方法须要一个字节切片([]byte),而须要写入的是一个字符串,因此要作类型转换。这种转换须要进行内存分配和内存复制,但复制后内存又会被立刻抛弃。这里就会有性能问题,这个内存分配会致使性能降低,须要避开这个内存分配。
在不少包中,实现了 io.Writer 的重要类,都会提供一个对应的高效写入字符串的 WriteString 方法,好比:内存
因为 Writer 接口并不包括 WriteString 方法,不能直接调用。这里能够先定义一个新的接口,这个接口只包含 WriteString 方法,而后使用类型断言来判断 w 的动态类型是否知足这个新接口:字符串
// 将s写入w,若是w有WriteString方法,就直接调用该方法 func writeString(w io.Writer, s string) (n int, err error) { type stringWriter interface { WriteString(string) (n int, err error) } if sw, ok := w.(stringWriter); ok { return sw.WriteString(s) // 避免了内存复制 } return w.Write([]byte(s)) // 分配了临时内存 } func writeMsg(w io.Writer, msg string) error { if _, err := writeString(w, msg); err != nil { return err } return nil }
因为上面的操做太常见了,io 包已经提供了一个函数 io.WriteString 能够直接使用。上面的代码能够简化为以下的方式:
func writeMsg2(w io.Writer, msg string) error { if _, err := io.WriteString(w, msg); err != nil { return err } return nil }
这里本质上仍是同样的,接口的定义、类型检查都封装到了io包的内部,而且内部实现的逻辑和上面是同样的。
以前想要向某个具体的类型写入字符串的时候,会查看该类型是否有 WriteString 方法。可是若是要操做的类型是 io.Writer 接口,虽然实际背后的动态类型仍是那个类型,可是就没法调用 WriteString 方法了。这时能够直接使用 io 包提供的工具函数完成一样的操做。
fmt.Fprint 系列的函数,接收的第一个参数是 io.Writer,也能够进行写入操做。并且 fmt 包提供的函数,接收空接口,能够是任何的类型,自带格式化成字符串的功能,不少时候更方便。 在向 os.Stdout 打印输出错误,以及 Web 服务器向客户端发送数据的时候使用的比较多,不过一样的也是能够向任何的 io.Writer 写入内容的,好比写入到文件。