GoTips--有关输入输出:bufio包

bufio

实现缓存IO

bufio的Reader和Writer操作一般适用于磁盘IO的读写场景。

Reader

bufio.Reader 结构包装了一个 io.Reader 对象,提供缓存功能,同时实现了 io.Reader 接 口。

type Reader struct

type Reader struct {
	buf          []byte
	rd           io.Reader // 底层的io.Reader
	//r:从buf中读走的字节(偏移);w:buf中填充内容的偏移;
	// w - r 是buf中可被读的长度(缓存数据的大小),也是Buffered()方法的返回值
	r, w         int       // buf read and write positions
	err          error
	lastByte     int // 最后一次读到的字节(ReadByte/UnreadByte)
	lastRuneSize int // 最后一次读到的Rune的大小(ReadRune/UnreadRune)
}

两个实例化 bufio.Reader 对象的函数:NewReaderNewReaderSize(NewReader 函数是调用 NewReaderSize 函数实现的)

func NewReader
func NewReader(rd io.Reader) *Reader

NewReader创建一个具有默认大小缓冲、从r读取的*Reader。

默认缓存大小:defaultBufSize=4096

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

NewReaderSize创建一个具有最少有size尺寸的缓冲、从r读取的Reader。如果参数r已经是一个具有足够大缓冲的 Reader类型值,会返回r。

func (*Reader) Reset
func (b *Reader) Reset(r io.Reader)

Reset丢弃缓冲中的数据,清除任何错误,将b重设为其下层从r读取数据。

func (*Reader) ReadSlice
func (b *Reader) ReadSlice(delim byte) (line []byte, err error)

ReadSlice从输入中读取,直到遇到第一个界定符(delim)为止,返回一个指向缓存中字节的 slice,在下次调用读操作(read)时,这些字节会无效。

func main() {
	reader := bufio.NewReader(strings.NewReader("http://studygolang.com. \nIt is the home of gophers"))
	line, _ := reader.ReadSlice('\n')
	fmt.Printf("the line:%s\n", line)
	n, _ := reader.ReadSlice('\n')
	fmt.Printf("the line:%s\n", line)
	fmt.Println(string(n))
}
the line:http://studygolang.com.

the line:It is the home of gophers
It is the home of gophers

从结果可以看出,第一次ReadSlice的结果(line),在第二次调用读操作后,内容发生了变化。

因为 ReadSlice返回的[]byte是指向Reader中的buffer,而不是copy一份返回

bufio.ReadSlice会把’\n’读取进来,自带换行

为避免一些错误尽量使用ReadBytesReadString

如果ReadSlice在找到界定符之前遇到了error,它就会返回缓存中所有的数据和错误本身(经常是 io.EOF)。如果在找到界定符之前缓存已经满了,ReadSlice会返回bufio.ErrBufferFull错 误。当且仅当返回的结果(line)没有以界定符结束的时候,ReadSlice返回err != nil,也就是 说,如果ReadSlice返回的结果line不是以界定符delim结尾,那么返回的err也一定不等于 nil(可能是bufio.ErrBufferFull或io.EOF)。例子代码:

reader := bufio.NewReaderSize(strings.NewReader("http://studygolang.com"),16)
line, err := reader.ReadSlice('\n')
fmt.Printf("line:%s\terror:%s\n", line, err)
line, err = reader.ReadSlice('\n')
fmt.Printf("line:%s\terror:%s\n", line, err)
line:http://studygola   error:bufio: buffer full
line:ng.com     error:EOF
func (*Reader) ReadBytes
func (b *Reader) ReadBytes(delim byte) (line []byte, err error)

ReadBytes读取直到第一次遇到delim字节,返回一个包含已读取的数据和delim字节的切片。如果ReadBytes方法在读取到delim之前遇到了错误,它会返回在错误之前读取的数据以及该错误(一般是io.EOF)。当且仅当ReadBytes方法返回的切片不以delim结尾时,会返回一个非nil的错误。

ReadBytes与ReadSlice不同

ReadSlice返回的[]byte是指向Reader中的buffer,而不是copy一份返回

ReadBytes返回的[]byte不会 是指向Reader中的buffer

正因为如此,通常使用ReadBytes或ReadString。

func main() {
	reader := bufio.NewReader(strings.NewReader("http://studygolang.com. \nIt is the home of gophers"))
	line, _ := reader.ReadBytes('\n')
	fmt.Printf("the line:%s\n", line)
	// 这里可以换上任意的 bufio 的 Read/Write 操作
	n, _ := reader.ReadBytes('\n')
	fmt.Printf("the line:%s\n", line)
	fmt.Println(string(n))
}
the line:http://studygolang.com.

the line:http://studygolang.com.

It is the home of gophers
func (*Reader) ReadString
func (b *Reader) ReadString(delim byte) (line string, err error)

ReadString读取直到第一次遇到delim字节,返回一个包含已读取的数据和delim字节的字符串。如果ReadString方法在读取到delim之前遇到了错误,它会返回在错误之前读取的数据以及该错误(一般是io.EOF)。当且仅当ReadString方法返回的切片不以delim结尾时,会返回一个非nil的错误。

func (*Reader) ReadLine
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)

ReadLine是一个低水平的行数据读取原语。大多数调用者应使用ReadBytes(’\n’)或ReadString(’\n’)代替,或者使用Scanner

func (*Reader) Buffered
func (b *Reader) Buffered() int

Buffered返回缓冲中现有的可读取的字节数。

func (*Reader) Peek
func (b *Reader) Peek(n int) ([]byte, error)

Peek返回输入流的下n个字节,而不会移动读取位置。返回的[]byte只在下一次调用读取操作前合法。如果Peek返回的切片长度比n小,它也会返会一个错误说明原因。如果n比缓冲尺寸还大,返回的错误将是ErrBufferFull。

注意:

和ReadSlice一样,返回的[]byte只是buffer中的引用,在下次IO操作后会无效,可见 该方法(以及ReadSlice这样的,返回buffer引用的方法)对多goroutine是不安全的,也就是在 多并发环境下,不能依赖其结果。

func main() {
	reader := bufio.NewReaderSize(strings.NewReader("http://studygolang.com.\t It is the home of gophers"), 14)
	go Peek(reader)
	time.Sleep(1)
	go reader.ReadBytes('\t')
	time.Sleep(1e8)
}

func Peek(reader *bufio.Reader) {
	line, _ := reader.Peek(14)
	fmt.Printf("%s\n", line)
	time.Sleep(2)
	fmt.Printf("%s\n", line)
}
http://studygo
ng.com.  It is

Writer

bufio.Writer 结构包装了一个 io.Writer 对象,提供缓存功能,同时实现了 io.Writer 接 口。

type Writer struct

type Writer struct {
	err error
	buf []byte
	n   int //当前缓存中的字节数
	wr  io.Writer // 底层的 io.Writer 对象
}

Example

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

Output:

Hello, world!
注意:

如果在写数据到 Writer 的时候出现了一个错误,不会再允许有数据被写进来了,并且所有 随后的写操作都会返回该错误。

和 Reader 类型一样,bufio 包提供了两个实例化 bufio.Writer 对象的函数:NewWriterNewWriterSize。其中,NewWriter 函数是调用 NewWriterSize 函数实现的:

func NewWriter
func NewWriter(w io.Writer) *Writer

NewWriter创建一个具有默认大小缓冲、写入w的*Writer。

func NewWriter(wr io.Writer) *Writer {
	// 默认缓存大小:defaultBufSize=4096
	return NewWriterSize(wr, defaultBufSize)
}
func NewWriterSize
func NewWriterSize(w io.Writer, size int) *Writer

NewWriterSize创建一个具有最少有size尺寸的缓冲、写入w的Writer。如果参数w已经是一个具有足够大缓冲的Writer类型值,会返回w。

func (*Writer) Buffered
func (b *Writer) Buffered() int

Buffered返回缓冲中已使用的字节数。

func (*Writer) Available
func (b *Writer) Available() int

Available返回缓冲中还有多少字节未使用。

func (*Writer) Reset
func (b *Writer) Reset(w io.Writer)

Reset丢弃缓冲中的数据,清除任何错误,将b重设为将其输出写入w。

func (*Writer) Flush
func (b *Writer) Flush() error

Flush方法将缓冲中的数据写入下层的io.Writer接口。

func (*Writer) Write
func (b *Writer) Write(p []byte) (nn int, err error)

Write将p的内容写入缓冲。返回写入的字节数。如果返回值nn < len§,还会返回一个错误说明原因。

func (*Writer) WriteString
func (b *Writer) WriteString(s string) (int, error)

WriteString写入一个字符串。返回写入的字节数。如果返回值nn < len(s),还会返回一个错误说明原因。

func (*Writer) WriteByte
func (b *Writer) WriteByte(c byte) error

WriteByte写入单个字节。

func (*Writer) WriteRune
func (b *Writer) WriteRune(r rune) (size int, err error)

WriteRune写入一个unicode码值(的utf-8编码),返回写入的字节数和可能的错误。

例子:

func main() {
	file, err := os.OpenFile("./t.txt", os.O_CREATE|os.O_RDWR, 0666)
	if err != nil {
		fmt.Println(err)
	}
	defer file.Close()

	content := []byte("hello world!")
	newWriter := bufio.NewWriterSize(file, 1024)
	if _, err = newWriter.Write(content); err != nil {
		fmt.Println(err)
	}
	if err = newWriter.Flush(); err != nil {
		fmt.Println(err)
	}
	fmt.Println("write file successful")
}

//勿忘newWriter.Flush();

type ReadWriter

type ReadWriter struct {
    *Reader
    *Writer
}

ReadWriter类型保管了指向Reader和Writer类型的指针,(因此)实现了io.ReadWriter接口。

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

NewReadWriter申请创建一个新的、将读写操作分派给r和w 的ReadWriter。

Scanner

分割函数

func ScanBytes
func ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error)

ScanBytes是用于Scanner类型的分割函数(符合SplitFunc),本函数会将每个字节作为一个token返回。

func ScanRunes
func ScanRunes(data []byte, atEOF bool) (advance int, token []byte, err error)

ScanRunes是用于Scanner类型的分割函数(符合SplitFunc),本函数会将每个utf-8编码的unicode码值作为一个token返回。本函数返回的rune序列和range一个字符串的输出rune序列相同。错误的utf-8编码会翻译为U+FFFD = “\xef\xbf\xbd”,但只会消耗一个字节。调用者无法区分正确编码的rune和错误编码的rune。

func ScanWords
func ScanWords(data []byte, atEOF bool) (advance int, token []byte, err error)

ScanRunes是用于Scanner类型的分割函数(符合SplitFunc),本函数会将空白(参见unicode.IsSpace)分隔的片段(去掉前后空白后)作为一个token返回。本函数永远不会返回空字符串。

func ScanLines
func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error)

ScanRunes是用于Scanner类型的分割函数(符合SplitFunc),本函数会将每一行文本去掉末尾的换行标记作为一个token返回。返回的行可以是空字符串。换行标记为一个可选的回车后跟一个必选的换行符。最后一行即使没有换行符也会作为一个token返回。

type Scanner struct

type Scanner struct {
	r            io.Reader // The reader provided by the client.
	split        SplitFunc // The function to split the tokens.
	maxTokenSize int       // Maximum size of a token; modified by tests.
	token        []byte    // Last token returned by split.
	buf          []byte    // Buffer used as argument to split.
	start        int       // First non-processed byte in buf.
	end          int       // End of data in buf.
	err          error     // Sticky error.
	empties      int       // Count of successive empty tokens.
	scanCalled   bool      // Scan has been called; buffer is in use.
	done         bool      // Scan has finished.
}
例子:
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)
	}
}
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)

}
//15

读取文件中的数据,一次读取一行,可以用Scanner实现:

func main() {
	file, err := os.Create("scanner.txt")
	if err != nil {
		panic(err)
	}
	defer file.Close()
	file.WriteString("http://studygolang.com.\nIt is the home of gophers.\nIf you are studying golang, welcome you!")
	// 将文件 offset 设置到文件开头
	file.Seek(0, os.SEEK_SET)
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		fmt.Println(scanner.Text())
	}
}
func NewScanner
func NewScanner(r io.Reader) *Scanner

NewScanner创建并返回一个从r读取数据的Scanner,默认的分割函数是ScanLines。

func (*Scanner) Split
func (s *Scanner) Split(split SplitFunc)

Split设置该Scanner的分割函数。本方法必须在Scan之前调用。

func (*Scanner) Scan
func (s *Scanner) Scan() bool

Scan方法获取当前位置的token(该token可以通过Bytes或Text方法获得),并让Scanner的扫描位置移动到下一个token。当扫描因为抵达输入流结尾或者遇到错误而停止时,本方法会返回false。在Scan方法返回false后,Err方法将返回扫描时遇到的任何错误;除非是io.EOF,此时Err会返回nil。

func (*Scanner) Bytes
func (s *Scanner) Bytes() []byte

Bytes方法返回最近一次Scan调用生成的token。底层数组指向的数据可能会被下一次Scan的调用重写。

func (*Scanner) Text
func (s *Scanner) Text() string

Bytes方法返回最近一次Scan调用生成的token,会申请创建一个字符串保存token并返回该字符串。

func (*Scanner) Err
func (s *Scanner) Err() error

Err返回Scanner遇到的第一个非EOF的错误。

[牛客输入输出练习-第七题](A+B(7)_OJ在线编程常见输入输出练习_牛客网 (nowcoder.com))

输入描述:

输入数据有多组, 每行表示一组输入数据。

每行不定有n个整数,空格隔开。(1 <= n <= 100)。

输出描述:

每组数据输出求和的结果

答案

package main
import (
    "fmt"
    "bufio"
    "os"
    "strconv"
    "strings"
)
func main(){
    newScan := bufio.NewScanner(os.Stdin)
    for{
        if ok := newScan.Scan();!ok{
            break
        }
        text := newScan.Text()
        numsStr := strings.Split(text," ")
        sum := 0
        for _,val := range numsStr{
            num,_ := strconv.Atoi(val)
            sum += num
        }
        fmt.Println(sum)
    }
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值