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)
}
}