Golang學習 - bufio 包

------------------------------------------------------------

// bufio 包實現了帶緩存的 I/O 操做

------------------------------------------------------------

type Reader struct { ... }

// NewReaderSize 將 rd 封裝成一個帶緩存的 bufio.Reader 對象,
// 緩存大小由 size 指定(若是小於 16 則會被設置爲 16)。
// 若是 rd 的基類型就是有足夠緩存的 bufio.Reader 類型,則直接將
// rd 轉換爲基類型返回。
func NewReaderSize(rd io.Reader, size int) *Reader

// NewReader 至關於 NewReaderSize(rd, 4096)
func NewReader(rd io.Reader) *Reader

// bufio.Reader 實現了以下接口:
// io.Reader
// io.WriterTo
// io.ByteScanner
// io.RuneScanner

// Peek 返回緩存的一個切片,該切片引用緩存中前 n 個字節的數據,
// 該操做不會將數據讀出,只是引用,引用的數據在下一次讀取操做之
// 前是有效的。若是切片長度小於 n,則返回一個錯誤信息說明緣由。
// 若是 n 大於緩存的總大小,則返回 ErrBufferFull。
func (b *Reader) Peek(n int) ([]byte, error)

// Read 從 b 中讀出數據到 p 中,返回讀出的字節數和遇到的錯誤。
// 若是緩存不爲空,則只能讀出緩存中的數據,不會從底層 io.Reader
// 中提取數據,若是緩存爲空,則:
// 一、len(p) >= 緩存大小,則跳過緩存,直接從底層 io.Reader 中讀
// 出到 p 中。
// 二、len(p) < 緩存大小,則先將數據從底層 io.Reader 中讀取到緩存
// 中,再從緩存讀取到 p 中。
func (b *Reader) Read(p []byte) (n int, err error)

// Buffered 返回緩存中未讀取的數據的長度。
func (b *Reader) Buffered() int

// Discard 跳事後續的 n 個字節的數據,返回跳過的字節數。
// 若是結果小於 n,將返回錯誤信息。
// 若是 n 小於緩存中的數據長度,則不會從底層提取數據。
func (b *Reader) Discard(n int) (discarded int, err error)

// ReadSlice 在 b 中查找 delim 並返回 delim 及其以前的全部數據。
// 該操做會讀出數據,返回的切片是已讀出的數據的引用,切片中的數據
// 在下一次讀取操做以前是有效的。
//
// 若是找到 delim,則返回查找結果,err 返回 nil。
// 若是未找到 delim,則:
// 一、緩存不滿,則將緩存填滿後再次查找。
// 二、緩存是滿的,則返回整個緩存,err 返回 ErrBufferFull。
//
// 若是未找到 delim 且遇到錯誤(一般是 io.EOF),則返回緩存中的所
// 有數據和遇到的錯誤。
//
// 由於返回的數據有可能被下一次的讀寫操做修改,因此大多數操做應該
// 使用 ReadBytes 或 ReadString,它們返回的是數據的拷貝。
func (b *Reader) ReadSlice(delim byte) (line []byte, err error)

// ReadLine 是一個低水平的行讀取原語,大多數狀況下,應該使用
// ReadBytes('\n') 或 ReadString('\n'),或者使用一個 Scanner。
//
// ReadLine 經過調用 ReadSlice 方法實現,返回的也是緩存的切片。用於
// 讀取一行數據,不包括行尾標記(\n 或 \r\n)。
//
// 只要能讀出數據,err 就爲 nil。若是沒有數據可讀,則 isPrefix 返回
// false,err 返回 io.EOF。
//
// 若是找到行尾標記,則返回查找結果,isPrefix 返回 false。
// 若是未找到行尾標記,則:
// 一、緩存不滿,則將緩存填滿後再次查找。
// 二、緩存是滿的,則返回整個緩存,isPrefix 返回 true。
//
// 整個數據尾部「有一個換行標記」和「沒有換行標記」的讀取結果是同樣。
//
// 若是 ReadLine 讀取到換行標記,則調用 UnreadByte 撤銷的是換行標記,
// 而不是返回的數據。
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)

// ReadBytes 功能同 ReadSlice,只不過返回的是緩存的拷貝。
func (b *Reader) ReadBytes(delim byte) (line []byte, err error)

// ReadString 功能同 ReadBytes,只不過返回的是字符串。
func (b *Reader) ReadString(delim byte) (line string, err error)

// Reset 將 b 的底層 Reader 從新指定爲 r,同時丟棄緩存中的全部數據,復位
// 全部標記和錯誤信息。 bufio.Reader。
func (b *Reader) Reset(r io.Reader)

------------------------------

// 示例:Peek、Read、Discard、Buffered
func main() {
	sr := strings.NewReader("ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
	buf := bufio.NewReaderSize(sr, 0)
	b := make([]byte, 10)

	fmt.Println(buf.Buffered()) // 0
	s, _ := buf.Peek(5)
	s[0], s[1], s[2] = 'a', 'b', 'c'
	fmt.Printf("%d   %q\n", buf.Buffered(), s) // 16   "abcDE"

	buf.Discard(1)

	for n, err := 0, error(nil); err == nil; {
		n, err = buf.Read(b)
		fmt.Printf("%d   %q   %v\n", buf.Buffered(), b[:n], err)
	}
	// 5   "bcDEFGHIJK"   <nil>
	// 0   "LMNOP"   <nil>
	// 6   "QRSTUVWXYZ"   <nil>
	// 0   "123456"   <nil>
	// 0   "7890"   <nil>
	// 0   ""   EOF
}

------------------------------

// 示例:ReadLine
func main() {
	sr := strings.NewReader("ABCDEFGHIJKLMNOPQRSTUVWXYZ\n1234567890")
	buf := bufio.NewReaderSize(sr, 0)

	for line, isPrefix, err := []byte{0}, false, error(nil); len(line) > 0 && err == nil; {
		line, isPrefix, err = buf.ReadLine()
		fmt.Printf("%q   %t   %v\n", line, isPrefix, err)
	}
	// "ABCDEFGHIJKLMNOP"   true   <nil>
	// "QRSTUVWXYZ"   false   <nil>
	// "1234567890"   false   <nil>
	// ""   false   EOF

	fmt.Println("----------")

	// 尾部有一個換行標記
	buf = bufio.NewReaderSize(strings.NewReader("ABCDEFG\n"), 0)

	for line, isPrefix, err := []byte{0}, false, error(nil); len(line) > 0 && err == nil; {
		line, isPrefix, err = buf.ReadLine()
		fmt.Printf("%q   %t   %v\n", line, isPrefix, err)
	}
	// "ABCDEFG"   false   <nil>
	// ""   false   EOF

	fmt.Println("----------")

	// 尾部沒有換行標記
	buf = bufio.NewReaderSize(strings.NewReader("ABCDEFG"), 0)

	for line, isPrefix, err := []byte{0}, false, error(nil); len(line) > 0 && err == nil; {
		line, isPrefix, err = buf.ReadLine()
		fmt.Printf("%q   %t   %v\n", line, isPrefix, err)
	}
	// "ABCDEFG"   false   <nil>
	// ""   false   EOF
}

------------------------------

// 示例:ReadSlice
func main() {
	// 尾部有換行標記
	buf := bufio.NewReaderSize(strings.NewReader("ABCDEFG\n"), 0)

	for line, err := []byte{0}, error(nil); len(line) > 0 && err == nil; {
		line, err = buf.ReadSlice('\n')
		fmt.Printf("%q   %v\n", line, err)
	}
	// "ABCDEFG\n"   <nil>
	// ""   EOF

	fmt.Println("----------")

	// 尾部沒有換行標記
	buf = bufio.NewReaderSize(strings.NewReader("ABCDEFG"), 0)

	for line, err := []byte{0}, error(nil); len(line) > 0 && err == nil; {
		line, err = buf.ReadSlice('\n')
		fmt.Printf("%q   %v\n", line, err)
	}
	// "ABCDEFG"   EOF
}

------------------------------------------------------------

type Writer struct { ... }

// NewWriterSize 將 wr 封裝成一個帶緩存的 bufio.Writer 對象,
// 緩存大小由 size 指定(若是小於 4096 則會被設置爲 4096)。
// 若是 wr 的基類型就是有足夠緩存的 bufio.Writer 類型,則直接將
// wr 轉換爲基類型返回。
func NewWriterSize(wr io.Writer, size int) *Writer

// NewWriter 至關於 NewWriterSize(wr, 4096)
func NewWriter(wr io.Writer) *Writer

// bufio.Writer 實現了以下接口:
// io.Writer
// io.ReaderFrom
// io.ByteWriter

// WriteString 功能同 Write,只不過寫入的是字符串
func (b *Writer) WriteString(s string) (int, error)

// WriteRune 向 b 寫入 r 的 UTF-8 編碼,返回 r 的編碼長度。
func (b *Writer) WriteRune(r rune) (size int, err error)

// Flush 將緩存中的數據提交到底層的 io.Writer 中
func (b *Writer) Flush() error

// Available 返回緩存中未使用的空間的長度
func (b *Writer) Available() int

// Buffered 返回緩存中未提交的數據的長度
func (b *Writer) Buffered() int

// Reset 將 b 的底層 Writer 從新指定爲 w,同時丟棄緩存中的全部數據,復位
// 全部標記和錯誤信息。至關於建立了一個新的 bufio.Writer。
func (b *Writer) Reset(w io.Writer)

------------------------------

// 示例:Available、Buffered、WriteString、Flush
func main() {
	buf := bufio.NewWriterSize(os.Stdout, 0)
	fmt.Println(buf.Available(), buf.Buffered()) // 4096 0

	buf.WriteString("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
	fmt.Println(buf.Available(), buf.Buffered()) // 4070 26

	// 緩存後統一輸出,避免終端頻繁刷新,影響速度
	buf.Flush() // ABCDEFGHIJKLMNOPQRSTUVWXYZ
}

------------------------------------------------------------

// ReadWriter 集成了 bufio.Reader 和 bufio.Writer
type ReadWriter struct {
	*Reader
	*Writer
}

// NewReadWriter 將 r 和 w 封裝成一個 bufio.ReadWriter 對象
func NewReadWriter(r *Reader, w *Writer) *ReadWriter

------------------------------------------------------------

// Scanner 提供了一個方便的接口來讀取數據,例如遍歷多行文本中的行。Scan 方法會經過
// 一個「匹配函數」讀取數據中符合要求的部分,跳過不符合要求的部分。「匹配函數」由調
// 用者指定。本包中提供的匹配函數有「行匹配函數」、「字節匹配函數」、「字符匹配函數」
// 和「單詞匹配函數」,用戶也能夠自定義「匹配函數」。默認的「匹配函數」爲「行匹配函
// 數」,用於獲取數據中的一行內容(不包括行尾標記)
//
// Scanner 使用了緩存,因此匹配部分的長度不能超出緩存的容量。默認緩存容量爲 4096 -
// bufio.MaxScanTokenSize,用戶能夠經過 Buffer 方法指定自定義緩存及其最大容量。
//
// Scan 在遇到下面的狀況時會終止掃描並返回 false(掃描一旦終止,將沒法再繼續):
// 一、遇到 io.EOF
// 二、遇到讀寫錯誤
// 三、「匹配部分」的長度超過了緩存的長度
//
// 若是須要對錯誤進行更多的控制,或「匹配部分」超出緩存容量,或須要連續掃描,則應該
// 使用 bufio.Reader
type Scanner struct { ... }

// NewScanner 建立一個 Scanner 來掃描 r,默認匹配函數爲 ScanLines。
func NewScanner(r io.Reader) *Scanner

// Buffer 用於設置自定義緩存及其可擴展範圍,若是 max 小於 len(buf),則 buf 的尺寸將
// 固定不可調。Buffer 必須在第一次 Scan 以前設置,不然會引起 panic。
// 默認狀況下,Scanner 會使用一個 4096 - bufio.MaxScanTokenSize 大小的內部緩存。
func (s *Scanner) Buffer(buf []byte, max int)

// Split 用於設置「匹配函數」,這個函數必須在調用 Scan 前執行。
func (s *Scanner) Split(split SplitFunc)

// SplitFunc 用來定義「匹配函數」,data 是緩存中的數據。atEOF 標記數據是否讀完。
// advance 返回 data 中已處理的數據的長度。token 返回找到的「匹配部分」,「匹配
// 部分」能夠是緩存的切片,也能夠是本身新建的數據(好比 bufio.errorRune)。「匹
// 配部分」將在 Scan 以後經過 Bytes 和 Text 反饋給用戶。err 返回錯誤信息。
//
// 若是在 data 中沒法找到一個完整的「匹配部分」則應返回 (0, nil, nil),以便告訴
// Scanner 向緩存中填充更多數據,而後再次掃描(Scan 會自動從新掃描)。若是緩存已
// 經達到最大容量尚未找到,則 Scan 會終止並返回 false。
// 若是 data 爲空,則「匹配函數」將不會被調用,意思是在「匹配函數」中沒必要考慮
// data 爲空的狀況。
//
// 若是 err != nil,掃描將終止,若是 err == ErrFinalToken,則 Scan 將返回 true,
// 表示掃描正常結束,若是 err 是其它錯誤,則 Scan 將返回 false,表示掃描出錯。錯誤
// 信息能夠在 Scan 以後經過 Err 方法獲取。
//
// SplitFunc 的做用很簡單,從 data 中找出你感興趣的數據,而後返回,同時返回已經處理
// 的數據的長度。
type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)

// Scan 開始一次掃描過程,若是匹配成功,能夠經過 Bytes() 或 Text() 方法取出結果,
// 若是遇到錯誤,則終止掃描,並返回 false。
func (s *Scanner) Scan() bool

// Bytes 將最後一次掃描出的「匹配部分」做爲一個切片引用返回,下一次的 Scan 操做會覆
// 蓋本次引用的內容。
func (s *Scanner) Bytes() []byte

// Text 將最後一次掃描出的「匹配部分」做爲字符串返回(返回副本)。
func (s *Scanner) Text() string

// Err 返回掃描過程當中遇到的非 EOF 錯誤,供用戶調用,以便獲取錯誤信息。
func (s *Scanner) Err() error

// ScanBytes 是一個「匹配函數」用來找出 data 中的單個字節並返回。
func ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error)

// ScanRunes 是一個「匹配函數」,用來找出 data 中單個 UTF8 字符的編碼。若是 UTF8 編
// 碼錯誤,則 token 會返回 "\xef\xbf\xbd"(即:U+FFFD),但只消耗 data 中的一個字節。
// 這使得調用者沒法區分「真正的U+FFFD字符」和「解碼錯誤的返回值」。
func ScanRunes(data []byte, atEOF bool) (advance int, token []byte, err error)

// ScanLines 是一個「匹配函數」,用來找出 data 中的單行數據並返回(包括空行)。
// 行尾標記能夠是 \n 或 \r\n(返回值不包含行尾標記)
func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error)

// ScanWords 是一個「匹配函數」,用來找出 data 中以空白字符分隔的單詞。
// 空白字符由 unicode.IsSpace 定義。
func ScanWords(data []byte, atEOF bool) (advance int, token []byte, err error)

------------------------------

// 示例:掃描
func main() {
	// 逗號分隔的字符串,最後一項爲空
	const input = "1,2,3,4,"
	scanner := bufio.NewScanner(strings.NewReader(input))
	// 定義匹配函數(查找逗號分隔的字符串)
	onComma := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
		for i := 0; i < len(data); i++ {
			if data[i] == ',' {
				return i + 1, data[:i], nil
			}
		}
		if atEOF {
			// 告訴 Scanner 掃描結束。
			return 0, data, bufio.ErrFinalToken
		} else {
			// 告訴 Scanner 沒找到匹配項,讓 Scan 填充緩存後再次掃描。
			return 0, nil, nil
		}
	}
	// 指定匹配函數
	scanner.Split(onComma)
	// 開始掃描
	for scanner.Scan() {
		fmt.Printf("%q ", scanner.Text())
	}
	// 檢查是否由於遇到錯誤而結束
	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "reading input:", err)
	}
}

------------------------------

// 示例:帶檢查掃描
func main() {
	const input = "1234 5678 1234567901234567890 90"
	scanner := bufio.NewScanner(strings.NewReader(input))
	// 自定義匹配函數
	split := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
		// 獲取一個單詞
		advance, token, err = bufio.ScanWords(data, atEOF)
		// 判斷其可否轉換爲整數,若是不能則返回錯誤
		if err == nil && token != nil {
			_, err = strconv.ParseInt(string(token), 10, 32)
		}
		// 這裏包含了 return 0, nil, nil 的狀況
		return
	}
	// 設置匹配函數
	scanner.Split(split)
	// 開始掃描
	for scanner.Scan() {
		fmt.Printf("%s\n", scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		fmt.Printf("Invalid input: %s", err)
	}
}

------------------------------------------------------------



相關文章
相關標籤/搜索