io.Reader
、io.Writer
定义了基于字节的读写行为,然而许多情况下,你会想要基于字符串、行来进行读写,这可以透过bufio
包的bufio.Reader
、bufio.Writer
等达到。
bufio.Reader
可以透过NewReader
、NewReaderSize
指定io.Reader
来创建实例,前者指定默认缓冲区大小 4096 字节调用后者,bufio.Reader
在读取来源时会从底层的io.Reader
将数据读入,在创建bufio.Reader
实例之后,可以使用的方法有:
func (b *Reader) Buffered() int
func (b *Reader) Discard(n int) (discarded int, err error)
func (b *Reader) Peek(n int) ([]byte, error)
func (b *Reader) Read(p []byte) (n int, err error)
func (b *Reader) ReadByte() (byte, error)
func (b *Reader) ReadBytes(delim byte) ([]byte, error)
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)
func (b *Reader) ReadRune() (r rune, size int, err error)
func (b *Reader) ReadSlice(delim byte) (line []byte, err error)
func (b *Reader) ReadString(delim byte) (string, error)
func (b *Reader) Reset(r io.Reader)
func (b *Reader) Size() int
func (b *Reader) UnreadByte() error
func (b *Reader) UnreadRune() error
func (b *Reader) WriteTo(w io.Writer) (n int64, err error)
因此对于逐行读取一个 UTF-8 文本文件来说,可以简单地编写如下:
package main
import (
"bufio"
"os"
"fmt"
"io"
)
func printFile(f *os.File) (err error){
var (
r = bufio.NewReader(f)
line string
)
for err == nil {
line, err = r.ReadString('\n')
fmt.Println(line)
}
if err == io.EOF {
err = nil
}
return
}
func main() {
var filename string
fmt.Print("文件名称:")
fmt.Scanf("%s", &filename);
f, err := os.Open(filename)
if err != nil {
panic(err)
}
defer f.Close()
printFile(f)
}
如果实际上是要读取之后写到另一个输出,使用WriteTo
方法更为方便:
package main
import (
"bufio"
"os"
"fmt"
)
func main() {
var filename string
fmt.Print("文件名称:")
fmt.Scanf("%s", &filename);
f, err := os.Open(filename)
if err != nil {
panic(err)
}
defer f.Close()
bufio.NewReader(f).WriteTo(os.Stdout)
}
Go 在io.WriteTo
接口定义了WriteTo
行为:
type WriterTo interface {
WriteTo(w Writer) (n int64, err error)
}
实际上bufio.Reader
实现了io
中一些接口,io.WriteTo
只是其中之一;类似地,如果要创建bufio.Writer
实例,可以透过NewWriter
、NewWriterSize
函数,创建之后可用的方法如下:
func (b *Writer) Available() int
func (b *Writer) Buffered() int
func (b *Writer) Flush() error
func (b *Writer) ReadFrom(r io.Reader) (n int64, err error)
func (b *Writer) Reset(w io.Writer)
func (b *Writer) Size() int
func (b *Writer) Write(p []byte) (nn int, err error)
func (b *Writer) WriteByte(c byte) error
func (b *Writer) WriteRune(r rune) (size int, err error)
func (b *Writer) WriteString(s string) (int, error)
bufio.Writer
实现了io
中一些接口,像是io.ReadFrom
,因此,也可以如下在标准输出中,显示读入的的文件内容:
package main
import (
"bufio"
"os"
"fmt"
)
func main() {
var filename string
fmt.Print("文件名称:")
fmt.Scanf("%s", &filename);
f, err := os.Open(filename)
if err != nil {
panic(err)
}
defer f.Close()
w := bufio.NewWriter(os.Stdout)
w.ReadFrom(f)
w.Flush()
}
NewWriter
默认的缓冲区为 4096 字节,由于这边使用标准输出,在缓冲区未满前,数据不会写出,可以使用Flush
来出清缓冲区中的数据。
事实上,对于需要逐行读取的需求,使用bufio.Scanner
会比较方便,可以使用NewScanner
来创建实例,创建之后有以下的方法可以使用:
func (s *Scanner) Buffer(buf []byte, max int)
func (s *Scanner) Bytes() []byte
func (s *Scanner) Err() error
func (s *Scanner) Scan() bool
func (s *Scanner) Split(split SplitFunc)
func (s *Scanner) Text() string
来看看读取文本文件的例子:
package main
import (
"bufio"
"os"
"fmt"
)
func main() {
var filename string
fmt.Print("文件名称:")
fmt.Scanf("%s", &filename);
f, err := os.Open(filename)
if err != nil {
panic(err)
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
panic(err)
}
}