這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
bufio資料讀取注意項
在go聖經第一章節 bufio-緩衝IO,有個例子,不讀源碼不容易理解。
DEMO 1
func main(){ reader :=bufio.NewReader( strings.NewReader(""http://studygolang.com. \nIt is the home of gophers, yes or no ?"), ) line, _ := reader.ReadSlice('\n') fmt.Printf("line=%s", line) // 注意點:bufio.ReadSlice會把'\n'讀取進來, 所以內建換行 n, _ := reader.ReadSlice('\n') fmt.Printf("the line:%s\n", line) // 猜猜line返回結果 fmt.Println(string(n)) // 沒有讀取到分行符號 return}
列印結果:
the line:http://studygolang.com. the line:It is the home of gophersIt is the home of gophers, yes or no ?
DEMO 2
type Reader struct { buf []byte rd io.Reader // reader provided by the client r, w int // buf read and write positions err error lastByte int lastRuneSize int } func main(){ reader :=bufio.NewReader( strings.NewReader(""http://studygolang.com. \nIt is the home of gophers, yes or no ?\n"), ) // 多加一個`\n` line, _ := reader.ReadSlice('\n') fmt.Printf("line=%s", line) // 注意點:bufio.ReadSlice會把'\n'讀取進來, 所以內建換行 n, _ := reader.ReadSlice('\n') fmt.Printf("the line:%s\n", line) // 猜猜line返回結果 fmt.Println(string(n)) // 讀取到分行符號 return}
列印結果:
the line:http://studygolang.com. the line:http://studygolang.com. It is the home of gophers, yes or no ?
其輸出結果對比,大跌眼鏡,居然有無\n結果,差距這麼大。
深入分析源碼:
func (b *Reader) ReadSlice(delim byte) (line []byte, err error){ for { if i:=bytes.IndexBytes(b.buf[b.r:b.w], delim); i>= 0{ line = b.buf[b.r : b.r+i+1] b.r =i+1 break } ... b.fill() // 如果buf已經讀完,需要再次從b.rd中讀取新的資料 }}func (b *Reader) fill() { if b.r > 0 { copy(b.buf, b.buf[b.r: b:w]) b.w -=b.r b.r=0 } ...}
以上為代碼關鍵點,我們現在分別討論:
- 對於第一個DEMO,第二個line返回的是
It is the home of gophers, yes or no ?,期望是返回 http://studygolang.com..
- 分析如下:因為第二次讀取reader.ReadSlice時,bytes.IndexByte沒有發現第二個
\n, 所以返回的索引i小於0, 則會執行fill方法,則時因為第一次讀取了資料,所以b.r肯定大於0,所以執行了copy操作,buf底層數組發生了變化,變成了新寫入的b.buf[b.r: b.w]值,但是要注意,line的長度和容量是不變的,所以會存在截斷或者不足的情況。
- 對於第二個DEMO,希望和期望的值是相同的
- 分析如下:因為第二次讀取reader.ReadSlice時,bytes.IndexByte可以發現第二個
\n, 則直接返回,寫沒有修改buf底層資料,也就使得line底層數組沒有發生變化。
結論:所以我們在使用標準庫時,不是很明白的方法要搞清楚才能好一些,不然容易犯錯誤。以下是規避ReadSlice錯誤的方法
func (b *Reader) ReadString(delim byte) (string, error) // 安全func (b *Reader) ReadBytes(delim byte) ([]byte, error) // 安全, 因為返回的[]byte是用make新分配記憶體空間