天天看點

Go語言從入門到規範-6.3、Go語言IO操作-io相關包和bufioGo語言從入門到規範-6.3、Go語言IO操作-io相關包和bufio

Go語言從入門到規範-6.3、Go語言IO操作-io相關包和bufio

文章目錄

  • Go語言從入門到規範-6.3、Go語言IO操作-io相關包和bufio
    • 1. 前言
    • 2. io、ioutil、bufio
    • 3. bufio
      • 3.1. 常量
      • 3.2. 變量
      • 3.3. func [ScanBytes](https://go-zh.org/src/bufio/scan.go?s=10504:10582#L270)
      • 3.4. func [ScanLines](https://go-zh.org/src/bufio/scan.go?s=13447:13525#L342)
      • 3.5. func [ScanRunes](https://go-zh.org/src/bufio/scan.go?s=11548:11626#L291)
      • 3.6. func [ScanWords](https://go-zh.org/src/bufio/scan.go?s=15081:15159#L392)
      • 3.7. type [ReadWriter](https://go-zh.org/src/bufio/bufio.go?s=23417:23461#L820)
        • (1). func [NewReadWriter](https://go-zh.org/src/bufio/bufio.go?s=23601:23653#L828)
      • 3.8. type [Reader](https://go-zh.org/src/bufio/bufio.go?s=1144:1356#L28)
        • (1). func [NewReader](https://go-zh.org/src/bufio/bufio.go?s=2262:2298#L63)
        • (2).func [NewReaderSize](https://go-zh.org/src/bufio/bufio.go?s=1836:1886#L46)
        • (3). func (*Reader) [Buffered](https://go-zh.org/src/bufio/bufio.go?s=8954:8985#L319)
        • (4). func (*Reader) [Discard](https://go-zh.org/src/bufio/bufio.go?s=4780:4838#L161)
        • (5). func (*Reader) [Peek](https://go-zh.org/src/bufio/bufio.go?s=4059:4103#L132)
        • (6). func (*Reader) [Read](https://go-zh.org/src/bufio/bufio.go?s=5610:5660#L199)
        • (7). func (*Reader) [ReadByte](https://go-zh.org/src/bufio/bufio.go?s=6469:6516#L240)
        • (8). func (*Reader) [ReadBytes](https://go-zh.org/src/bufio/bufio.go?s=14471:14534#L449)
        • (9). func (*Reader) [ReadLine](https://go-zh.org/src/bufio/bufio.go?s=12819:12886#L402)
        • (10). func (*Reader) [ReadRune](https://go-zh.org/src/bufio/bufio.go?s=7623:7680#L280)
        • (11). func (*Reader) [ReadSlice](https://go-zh.org/src/bufio/bufio.go?s=10277:10340#L338)
        • (12). func (*Reader) [ReadString](https://go-zh.org/src/bufio/bufio.go?s=16147:16211#L502)
        • (13). func (*Reader) [Reset](https://go-zh.org/src/bufio/bufio.go?s=2552:2587#L71)
        • (14). func (*Reader) [UnreadByte](https://go-zh.org/src/bufio/bufio.go?s=6890:6925#L257)
        • (15). func (*Reader) [UnreadRune](https://go-zh.org/src/bufio/bufio.go?s=8624:8659#L306)
        • (16). func (*Reader) [WriteTo](https://go-zh.org/src/bufio/bufio.go?s=16361:16419#L511)
      • 3.9. type [Scanner](https://go-zh.org/src/bufio/scan.go?s=2051:2595#L33)
        • (1). 示例 (Custom)
        • (2). 示例 (Lines)
        • (3). 示例 (Words)
        • (4). func [NewScanner](https://go-zh.org/src/bufio/scan.go?s=5252:5289#L96)
        • (5). func (*Scanner) [Bytes](https://go-zh.org/src/bufio/scan.go?s=6024:6056#L121)
        • (6). func (*Scanner) [Err](https://go-zh.org/src/bufio/scan.go?s=5611:5640#L108)
        • (7). func (*Scanner) [Scan](https://go-zh.org/src/bufio/scan.go?s=7413:7442#L150)
        • (8). func (*Scanner) [Split](https://go-zh.org/src/bufio/scan.go?s=10207:10247#L260)
        • (9). func (*Scanner) [Text](https://go-zh.org/src/bufio/scan.go?s=6324:6355#L130)
      • 3.10. type [SplitFunc](https://go-zh.org/src/bufio/scan.go?s=4456:4539#L76)
      • 3.11. type [Writer](https://go-zh.org/src/bufio/bufio.go?s=17994:18063#L579)
        • 示例
        • (1). func [NewWriter](https://go-zh.org/src/bufio/bufio.go?s=18846:18881#L610)
        • (2). func [NewWriterSize](https://go-zh.org/src/bufio/bufio.go?s=18459:18508#L592)
        • (3). func (*Writer) [Available](https://go-zh.org/src/bufio/bufio.go?s=19772:19804#L656)
        • (4). func (*Writer) [Buffered](https://go-zh.org/src/bufio/bufio.go?s=19983:20014#L661)
        • (5). func (*Writer) [Flush](https://go-zh.org/src/bufio/bufio.go?s=19241:19271#L625)
        • (6). func (*Writer) [ReadFrom](https://go-zh.org/src/bufio/bufio.go?s=22488:22547#L770)
        • (7). func (*Writer) [Reset](https://go-zh.org/src/bufio/bufio.go?s=19035:19070#L616)
        • (8). func (*Writer) [Write](https://go-zh.org/src/bufio/bufio.go?s=20384:20436#L671)
        • (9). func (*Writer) [WriteByte](https://go-zh.org/src/bufio/bufio.go?s=20893:20933#L698)
        • (10). func (*Writer) [WriteRune](https://go-zh.org/src/bufio/bufio.go?s=21266:21322#L714)
        • (11). func (*Writer) [WriteString](https://go-zh.org/src/bufio/bufio.go?s=22121:22172#L749)
      • 3.12. 封包件

1. 前言

Go的IO通過我們之前學習總結的Go常用包可以看出來基本有io、ioutil、bufio三個,bufio是我們在業務層面開發時需要學習了解的重點,本文的重點也是bufio,對于io和ioutil有相關性,是以簡單帶過,目前不做專門總結,感興趣的小夥伴可以自行學習。(關于I/O這裡就不多說了,簡單了解就是資料的輸入/輸出,一般檔案操作、網絡程式設計學習前都需要先了解I/O,因為檔案和網絡等都牽扯到資料的I/O,我們之前已經在os包中接觸過了file操作的基本接口,這裡我們再對I/O做簡單的了解和總結,友善了解常用的檔案操作和I/O接口如何結合使用,靈活進行檔案讀寫等操作,以及為後續學習net/http/rpc等包相關内容做鋪墊)

2. io、ioutil、bufio

官網标準包位址:

  • io:https://go-zh.org/pkg/io/

io 包為I/O原語提供了基礎的接口. 它主要包裝了這些原語的已有實作,如 os 包中的那些,抽象成函數性的共享公共接口, 加上一些其它相關的原語。

由于這些接口和原語以不同的實作包裝了低級操作,是以除非另行通知, 否則客戶不應假定它們對于并行執行是安全的。

是以我們直接使用io包的情況很少,隻是提供了基礎接口便于我們擴充,也就是我們也可以利用io實作ioutil、bufio等類似功能。

  • ioutil:https://go-zh.org/pkg/io/ioutil/

ioutil 實作了一些I/O的工具函數。沒有特殊業務需要的話,其實ioutil還是蠻有用的,簡單粗暴。

  • bufio:https://go-zh.org/pkg/bufio/

bufio 包實作了帶緩存的I/O操作. 它封裝了一個io.Reader或者io.Writer對象,另外建立了一個對象 (Reader或者Writer),這個對象也實作了一個接口,并提供緩沖和文檔讀寫的幫助。

3. bufio

3.1. 常量

const (
    // MaxScanTokenSize is the maximum size used to buffer a token.
    // The actual maximum token size may be smaller as the buffer
    // may need to include, for instance, a newline.
    MaxScanTokenSize = 64 * 1024
)
           

3.2. 變量

var (
    ErrInvalidUnreadByte = errors.New("bufio: invalid use of UnreadByte")
    ErrInvalidUnreadRune = errors.New("bufio: invalid use of UnreadRune")
    ErrBufferFull        = errors.New("bufio: buffer full")
    ErrNegativeCount     = errors.New("bufio: negative count")
)
var (
    ErrTooLong         = errors.New("bufio.Scanner: token too long")
    ErrNegativeAdvance = errors.New("bufio.Scanner: SplitFunc returns negative advance count")
    ErrAdvanceTooFar   = errors.New("bufio.Scanner: SplitFunc returns advance count beyond input")
)
           

Errors returned by Scanner.

3.3. func ScanBytes

ScanBytes是用于Scanner類型的分割函數(符合SplitFunc), 本函數會将每個位元組作為一個token傳回。

3.4. func ScanLines

ScanLines是用于Scanner類型的分割函數(符合SplitFunc),本函數會将每一行文本去掉末尾的換行标記作為一個token傳回。傳回的行可以是空字元串。換行标記為一個可選的回車後跟一個必選的換行符。最後一行即使沒有換行符也會作為一個token傳回。

3.5. func ScanRunes

ScanRunes是用于Scanner類型的分割函數(符合SplitFunc), 本函數會将每個utf-8編碼的unicode碼值作為一個token傳回。 本函數傳回的rune序列和range一個字元串的輸出rune序列相同。 錯誤的utf-8編碼會翻譯為U+FFFD = “\xef\xbf\xbd”, 但隻會消耗一個位元組。調用者無法區分正确編碼的rune和錯誤編碼的rune。

3.6. func ScanWords

ScanWords是用于Scanner類型的分割函數(符合SplitFunc), 本函數會将每一行文本去掉末尾的換行标記作為一個token傳回。 傳回的行可以是空字元串。換行标記為一個可選的回車後跟一個必選的換行符。 最後一行即使沒有換行符也會作為一個token傳回。

3.7. type ReadWriter

type ReadWriter struct {
    *Reader
    *Writer
}
           

ReadWriter存儲輸入輸出指針。 它實作了io.ReadWriter。

(1). func NewReadWriter

func NewReadWriter(r *Reader, w *Writer) *ReadWriter
           

NewReadWriter配置設定新的ReadWriter來進行r和w的排程。

3.8. type Reader

type Reader struct {
    // contains filtered or unexported fields
}
           

Reader實作了對一個io.Reader對象的緩沖讀。

(1). func NewReader

func NewReader(rd io.Reader) *Reader
           

NewReader傳回一個新的Reader,這個Reader的大小是預設的大小。

(2).func NewReaderSize

func NewReaderSize(rd io.Reader, size int) *Reader
           

NewReaderSize傳回了一個新的讀取器,這個讀取器的緩存大小至少大于制定的大小。 如果io.Reader參數已經是一個有足夠大緩存的讀取器,它就會傳回這個Reader了。

(3). func (*Reader) Buffered

Buffered傳回目前緩存的可讀位元組數。

(4). func (*Reader) Discard

Discard跳過後n個位元組,傳回丢棄的位元組數。

如果Discard跳過小于n個位元組,它也會傳回一個錯誤。如果0 <= n <= b.b buffered(),則Discard保證不會從底層io.Reader中讀取。

(5). func (*Reader) Peek

Peek傳回沒有讀取的下n個位元組。在下個讀取的調用前,位元組是不可見的。如果Peek傳回的位元組數少于n, 它一定會解釋為什麼讀取的位元組數段了。如果n比b的緩沖大小更大,傳回的錯誤是ErrBufferFull。

(6). func (*Reader) Read

Read讀取資料到p。 傳回讀取到p的位元組數。 底層讀取最多隻會調用一次Read,是以n會小于len§。 在EOF之後,調用這個函數傳回的會是0和io.Eof。

(7). func (*Reader) ReadByte

ReadByte讀取和回複一個單位元組。 如果沒有位元組可以讀取,傳回一個error。

(8). func (*Reader) ReadBytes

ReadBytes讀取輸入到第一次終止符發生的時候,傳回的slice包含從目前到終止符的内容(包括終止符)。 如果ReadBytes在遇到終止符之前就捕獲到一個錯誤,它就會傳回遇到錯誤之前已經讀取的資料,和這個捕獲 到的錯誤(經常是 io.EOF)。當傳回的資料沒有以終止符結束的時候,ReadBytes傳回err != nil。 對于簡單的使用,或許 Scanner 更友善。

(9). func (*Reader) ReadLine

ReadLine是一個底層的原始讀取指令。許多調用者或許會使用ReadBytes(’\n’)或者ReadString(’\n’)來代替這個方法。

ReadLine嘗試傳回單個行,不包括行尾的最後一個分隔符。如果一個行大于緩存,調用的時候傳回了ifPrefix, 就會傳回行的頭部。行剩餘的部分就會在下次調用的時候傳回。當調用行的剩餘的部分的時候,isPrefix将會設為false, 傳回的緩存隻能在下次調用ReadLine的時候看到。ReadLine會傳回了一個非空行,或者傳回一個error, 但是不會兩者都傳回。

ReadLine傳回的文本不會包含行結尾("\r\n"或者"\n")。如果輸入沒有最終的行結尾的時候,不會傳回 任何迹象或者錯誤。在 ReadLine 之後調用 UnreadByte 将總是放回讀取的最後一個位元組 (可能是屬于該行末的字元),即便該位元組并非 ReadLine 傳回的行的一部分。

(10). func (*Reader) ReadRune

ReadRune讀取單個的UTF-8編碼的Unicode位元組,并且傳回rune和它的位元組大小。 如果編碼的rune是可見的,它消耗一個位元組并且傳回1位元組的unicode.ReplacementChar (U+FFFD)。

(11). func (*Reader) ReadSlice

ReadSlice從輸入中讀取,直到遇到第一個終止符為止,傳回一個指向緩存中位元組的slice。在下次調用的時候這些位元組就是已經被讀取了。如果ReadSlice在找到終止符之前遇到了error, 它就會傳回緩存中所有的資料和錯誤本身(經常是 io.EOF)。 如果在終止符之前緩存已經被充滿了,ReadSlice會傳回ErrBufferFull錯誤。由于ReadSlice傳回的資料會被下次的I/O操作重寫,是以許多的用戶端會選擇使用ReadBytes或者ReadString代替。當且僅當資料沒有以終止符結束的時候,ReadSlice傳回err != nil

(12). func (*Reader) ReadString

ReadString讀取輸入到第一次終止符發生的時候,傳回的string包含從目前到終止符的内容(包括終止符)。 如果ReadString在遇到終止符之前就捕獲到一個錯誤,它就會傳回遇到錯誤之前已經讀取的資料,和這個捕獲 到的錯誤(經常是 io.EOF)。當傳回的資料沒有以終止符結束的時候,ReadString傳回err != nil。 對于簡單的使用,或許 Scanner 更友善。

(13). func (*Reader) Reset

Reset丢棄緩沖中的資料,清除任何錯誤,将b重設為其下層從r讀取資料。

(14). func (*Reader) UnreadByte

UnreadByte将最後的位元組标志為未讀。隻有最後的位元組才可以被标志為未讀。

(15). func (*Reader) UnreadRune

UnreadRune将最後一個rune設定為未讀。如果最新的在buffer上的操作不是ReadRune,則UnreadRune 就傳回一個error。(在這個角度上看,這個函數比UnreadByte更嚴格,UnreadByte會将最後一個讀取 的byte設定為未讀。)

(16). func (*Reader) WriteTo

WriteTo實作了io.WriterTo。

3.9. type Scanner

type Scanner struct {
    // contains filtered or unexported fields
}
           

Scanner類型提供了友善的讀取資料的接口,如從換行符分隔的文本裡讀取每一行。

成功調用的Scan方法會逐漸提供檔案的token, 跳過token之間的位元組。token由SplitFunc類型的分割函數指定; 預設的分割函數會将輸入分割為多個行,并去掉行尾的換行标志。 本包預定義的分割函數可以将檔案分割為行、位元組、unicode碼值、空白分隔的word。 調用者可以定制自己的分割函數。

掃描會在抵達輸入流結尾、遇到的第一個I/O錯誤、token過大不能儲存進緩沖時, 不可恢複的停止。當掃描停止後,目前讀取位置可能會遠在最後一個獲得的token後面。 需要更多對錯誤管理的控制或token很大,或必須從reader連續掃描的程式, 應使用bufio.Reader代替。

(1). 示例 (Custom)

使用帶有自定義拆分函數Scanner(通過對ScanWords進行換行建構)來驗證32位十進制輸入。

代碼:

// An artificial input source.
const input = "1234 5678 1234567901234567890"
scanner := bufio.NewScanner(strings.NewReader(input))
// Create a custom split function by wrapping the existing ScanWords function.
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
}
// Set the split function for the scanning operation.
scanner.Split(split)
// Validate the input
for scanner.Scan() {
    fmt.Printf("%s\n", scanner.Text())
}

if err := scanner.Err(); err != nil {
    fmt.Printf("Invalid input: %s", err)
}
           

輸出:

1234
5678
Invalid input: strconv.ParseInt: parsing "1234567901234567890": value out of range
           

(2). 示例 (Lines)

Scanner的最簡單用途,将标準輸入作為一組行讀取。

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        fmt.Println(scanner.Text()) // Println will add back the final '\n'
    }
    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, "reading standard input:", err)
    }
}
           

(3). 示例 (Words)

通過掃描以空格分隔的符号序列的形式輸入,使用Scanner程式來實作一個簡單的單詞計數實用程式。

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

func main() {
    // An artificial input source.
    const input = "Now is the winter of our discontent,\nMade glorious summer by this sun of York.\n"
    scanner := bufio.NewScanner(strings.NewReader(input))
    // Set the split function for the scanning operation.
    scanner.Split(bufio.ScanWords)
    // Count the words.
    count := 0
    for scanner.Scan() {
        count++
    }
    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, "reading input:", err)
    }
    fmt.Printf("%d\n", count)
}
           

(4). func NewScanner

func NewScanner(r io.Reader) *Scanner
           

NewScanner建立并傳回一個從r讀取資料的Scanner,預設的分割函數是ScanLines。

(5). func (*Scanner) Bytes

Bytes方法傳回最近一次Scan調用生成的token。 底層數組指向的資料可能會被下一次Scan的調用重寫。

(6). func (*Scanner) Err

Err傳回Scanner遇到的第一個非EOF的錯誤。

(7). func (*Scanner) Scan

Scan方法擷取目前位置的token(該token可以通過Bytes或Text方法獲得), 并讓Scanner的掃描位置移動到下一個token。 當掃描因為抵達輸入流結尾或者遇到錯誤而停止時, 本方法會傳回false。在Scan方法傳回false後, Err方法将傳回掃描時遇到的任何錯誤;除非是io.EOF,此時Err會傳回nil。 若 split 函數傳回了 100 個空标記而沒有推進輸入,那麼它就會派錯(panic)。這是 scanner 的一個常見錯誤。

(8). func (*Scanner) Split

Split設定該Scanner的分割函數。本方法必須在Scan之前調用。

(9). func (*Scanner) Text

Bytes方法傳回最近一次Scan調用生成的token, 會申請建立一個字元串儲存token并傳回該字元串。

3.10. type SplitFunc

SplitFunc類型代表用于對輸出作詞法分析的分割函數。

參數data是尚未處理的資料的一個開始部分的切片, 參數atEOF表示是否Reader接口不能提供更多的資料。 傳回值是解析位置前進的位元組數,将要傳回給調用者的token切片, 以及可能遇到的錯誤。如果資料不足以(保證)生成一個完整的token, 例如需要一整行資料但data裡沒有換行符, SplitFunc可以傳回(0, nil, nil)來告訴Scanner讀取更多的資料 寫入切片然後用從同一位置起始、長度更長的切片再試一次(調用SplitFunc類型函數)。

如果傳回值err非nil,掃描将終止并将該錯誤傳回給Scanner的調用者。

除非atEOF為真,永遠不會使用空切片data調用SplitFunc類型函數。 然而,如果atEOF為真,data卻可能是非空的、且包含着未處理的文本。

3.11. type Writer

type Writer struct {
    // contains filtered or unexported fields
}
           

Writer實作了io.Writer對象的緩存。 如果在寫資料到Writer的時候出現了一個錯誤,不會再有資料被寫進來了, 并且所有随後的寫操作都會傳回error。當所有資料被寫入後,用戶端應調用 Flush 方法以確定所有資料已轉為基本的 io.Writer

示例

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    w := bufio.NewWriter(os.Stdout)
    fmt.Fprint(w, "Hello, ")
    fmt.Fprint(w, "world!")
    w.Flush() // Don't forget to flush!
}
           

(1). func NewWriter

func NewWriter(w io.Writer) *Writer
           

NewWriter傳回一個新的,有預設尺寸緩存的Writer。

(2). func NewWriterSize

func NewWriterSize(w io.Writer, size int) *Writer
           

NewWriterSize傳回一個新的Writer,它的緩存一定大于指定的size參數。 如果io.Writer參數已經是足夠大的有緩存的Writer了,函數就會傳回它底層的Writer。

(3). func (*Writer) Available

Available傳回buffer中有多少的位元組數未使用。

(4). func (*Writer) Buffered

Buffered傳回已經寫入到目前緩存的位元組數。

(5). func (*Writer) Flush

Flush将緩存上的所有資料寫入到底層的io.Writer中。

(6). func (*Writer) ReadFrom

ReadFrom實作了io.ReaderFrom。

(7). func (*Writer) Reset

Reset将丢棄任何未重新整理的緩沖資料,清除任何錯誤,并重置b以将其輸出寫入w。

(8). func (*Writer) Write

Writer将p中的内容寫入到緩存中。 它傳回寫入的位元組數。 如果nn < len§, 它也會傳回錯誤,用于解釋為什麼寫入的資料會短缺。

(9). func (*Writer) WriteByte

WriterByte寫單個位元組。

(10). func (*Writer) WriteRune

WriteRune寫單個的Unicode代碼,傳回寫的位元組數,和遇到的錯誤。

(11). func (*Writer) WriteString

WriteString寫一個string。 它傳回寫入的位元組數。 如果位元組數比len(s)少,它就會傳回error來解釋為什麼寫入的資料短缺了。

3.12. 封包件

bufio.go

scan.go