这篇文章是对区块链开发中Go语言之IO操作做一个梳理,每一个的地方,每一种的知识,每一种事物,都是从陌生到熟悉。在这个过程里面,或许能开阔眼界,增长见识,体验乐趣,下面我们就一起来看看吧。
type Reader interface { Read(p []byte) (n int, err error)}
实现了Reader接口的都可以用read方法,将数据读入到p字节数组,n表示读取了几个字节,err返回错误。 如果读到了文件尾EOF,则err返回EOF。
注意,当文件最后一小段已经无法填满p这个字节数组时,不会产生EOF的错误,只会在下一次读取时产生n=0,err=io.EOF的错误
举例
func main() { file, _ := os.Open("main.go") var a [128]byte count:=0 for { n, err := file.Read(a[:]) count+=1 if err != nil { if err == io.EOF { break } else { os.Exit(1) } } fmt.Printf("%s/n", a[:n]) } fmt.Printf("%d/n", count)}
type Writer interface { Write(p []byte) (n int, err error)}
Write 将 len(p) 个字节从 p 中写入到基本数据流中。它返回从 p 中被写入的字节数 n(0 常见错误原因有磁盘满了
和Reader,Writer类似,但是需要自己调控偏移量。
注意:接近文件尾巴时,当n小于数组大小时也触发了err.EOF,需要自行把最后n小于数组大小的这点数据处理一下。
举例:
func main() { file, _ := os.Open("main.go") var a [128]byte count := 0 var pos int64 = 0 for { n, err := file.ReadAt(a[:], pos) count += 1 pos += int64(n) if err != nil { if err == io.EOF { fmt.Printf("%s", a[:n]) //区别在这里 break } else { os.Exit(1) } } fmt.Printf("%s", a[:n]) } fmt.Println() fmt.Printf("%d", count)}
一次性读完直到EOF,或者写入全部数据
type Seeker interface { Seek(offset int64, whence int) (ret int64, err error)}
用来设置偏移量,也就是从哪开始读,offset由whence解释。
读或写一个字节
一次性读取数据
读取目录并返回排好序的文件和子目录名
func WriteFile(filename string, data []byte, perm os.FileMode) error
这里特别注意的是写文件的权限问题,perm的数值,和linux规则一致 四位(777):
模式 | 数字 |
---|---|
rwx | 7 |
rw- | 6 |
r-x | 5 |
r-- | 4 |
-wx | 3 |
-w- | 2 |
--x | 1 |
--- | 0 |
组合如0666,表示rw-rw-rw- |
|
是io库的包装,提供带缓存的方法
后三个方法最终都是调用ReadSlice来实现的
func (b *Reader) ReadSlice(delim byte) (line []byte, err error)
示例:
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)// 这里可以换上任意的 bufio 的 Read/Write 操作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 gophersIt is the home of gophers
注意ReadSlice每次返回的line是指向同一个缓存数组,因此ReadSlice的实现是反复覆盖重写缓存数组。
如果ReadSlice在找到分界符前
func (b *Reader) ReadBytes(delim byte) (line []byte, err error)
返回的byte是copy的一份数组
从以下实验可看出来
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
是对ReadBytes的封装,将返回的line转换成string
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)
这里要说的是isPrefix,用于读取的一行超过了缓存大小,则isPrefix为true,下次还读这行余下的部分,直到读完这行才isPrefix返回false
ReadLine返回的文本不会包含行结尾("/r/n"或者"/n")
该方法只是“窥探”一下Reader中没有读取的n个字节。好比栈数据结构中的取栈顶元素,但不出栈。
func (b *Reader) Peek(n int) ([]byte, error)
同上面介绍的ReadSlice一样,返回的[]byte只是buffer中的引用。所以在并发的时候有可能就被别人给改了
用于方便的按token读取数据,token的分词规则用SplitFunc定义。默认按行分词,会去掉末尾换行符。 了解Scanner前要先了解SplitFunc
type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)
SplitFunc 定义了 用于对输入进行分词的 split 函数的签名。
参数
返回值
举例
func main() { // Comma-separated list; last entry is empty. const input = "1,2,3,4," scanner := bufio.NewScanner(strings.NewReader(input)) // Define a split function that separates on commas. onComma := func(data []byte, atEOF bool) (advance int, token []byte, err error) { for i := 0; i输出
"1" "2" "3" "4" "5"你也可以用系统定义好的几个分割token的方法。
ScanBytes 返回单个字节作为一个 token。
ScanRunes 返回单个 UTF-8 编码的 rune 作为一个 token。返回的 rune 序列(token)和 range string类型 返回的序列是等价的,也就是说,对于无效的 UTF-8 编码会解释为 U+FFFD = "/xef/xbf/xbd"。
ScanWords 返回通过“空格”分词的单词。如:study golang,调用会返回study。注意,这里的“空格”是 unicode.IsSpace(),即包括:'/t', '/n', '/v', '/f', '/r', ' ', U+0085 (NEL), U+00A0 (NBSP)。
ScanLines 返回一行文本,不包括行尾的换行符。这里的换行包括了Windows下的"/r/n"和Unix下的"/n"。
Scanner 的使用方法
- NewScanner
- Split设置分割token的方法
- 循环scanner.Scan()
- 在循环里用scanner.Text()取token 示例
const input = "This is The Golang Standard Library./nWelcome you!"scanner := bufio.NewScanner(strings.NewReader(input))scanner.Split(bufio.ScanWords)count := 0for scanner.Scan() { count++}if err := scanner.Err(); err != nil { fmt.Fprintln(os.Stderr, "reading input:", err)}fmt.Println(count)Writer
带缓存的writer,记得在最终的写入操作执行完后flush一下,确保全部缓存都真正写入,如果大家想了解更多精彩内容,尽在https://js.Vevb.com。
新闻热点
疑难解答