Go xmas2020 学习笔记 11、io.Reader
课程地址 go-class-slides/xmas-2020 at trunk · matt4biz/go-class-slides (github.com)
主讲老师 Matt Holiday
11-Homework #2
package main
import (
"bytes"
"fmt"
"os"
"strings"
"golang.org/x/net/html"
)
var raw = `
<!DOCTYPE html>
<html>
<body>
<h1>My First Heading</h1>
<p>My first paragraph.</p>
<p>HTML <a href="https://www.w3schools.com/html/html_images.asp">images</a> are defined with the img tag:</p>
<img src="xxx.jpg" width="104" height="142">
</body>
</html>
`
func visit(n *html.Node, words, pics *int) {
if n.Type == html.TextNode {
*words += len(strings.Fields(n.Data))
} else if n.Type == html.ElementNode && n.Data == "img" {
*pics++
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
visit(c, words, pics)
}
}
func countWordsAndImages(doc *html.Node) (int, int) {
var words, pics int
visit(doc, &words, &pics)
return words, pics
}
func main() {
doc, err := html.Parse(bytes.NewReader([]byte(raw)))
if err != nil {
fmt.Fprintf(os.Stderr, "parse failed:%s\n", err)
os.Exit(-1)
}
words, pics := countWordsAndImages(doc)
fmt.Printf("%d words and %d images\n", words, pics)
}
14 words and 1 images
假如我去访问一个网站,我会得到一个字节的片段,将它放到阅读器中。
doc, err := html.Parse(bytes.NewReader([]byte(raw)))
返回的\(doc\)是树节点,我们可以用 \(for\) 循环通过节点的 \(FirstChild、NextSibling\) 属性遍历整棵树。
11-Reader
Reader interface
上文出现了阅读器这个概念,我感到很模糊,于是查找相关资料进行学习。
type Reader interface {
Read(p []byte) (n int ,err error)
}
官方文档中关于该接口方法的说明
Read 将 len(p) 个字节读取到 p 中。它返回读取的字节数 n(0 <= n <= len(p)) 以及任何遇到的错误。即使 Read 返回的 n < len(p),它也会在调用过程中使用 p 的全部作为暂存空间。若一些数据可用但不到 len(p) 个字节,Read 会照例返回可用的数据,而不是等待更多数据。
Read 在成功读取 n > 0 个字节后遇到一个错误或
EOF (end-of-file)
,它就会返回读取的字节数。它会从相同的调用中返回(非nil的)错误或从随后的调用中返回错误(同时 n == 0)。 一般情况的一个例子就是 Reader 在输入流结束时会返回一个非零的字节数,同时返回的err
不是EOF
就是nil
。无论如何,下一个 Read 都应当返回0, EOF
。
调用者应当总在考虑到错误 err 前处理 n > 0 的字节。这样做可以在读取一些字节,以及允许的 EOF 行为后正确地处理 I/O 错误
PS: 当Read
方法返回错误时,不代表没有读取到任何数据,可能是数据被读完了时返回的io.EOF
。
Reader 接口的方法集(Method_sets)只包含一个 Read 方法,因此,所有实现了 Read
方法的类型都实现了io.Reader
接口,也就是说,在所有需要 io.Reader
的地方,可以传递实现了 Read()
方法的类型的实例。
NewReader func
Reader Struct
NewReader创建一个从s读取数据的Reader
type Reader struct {
s string //对应的字符串
i int64 // 当前读取到的位置
prevRune int
}
Len 、Size,Read func
Len作用: 返回未读的字符串长度
Size的作用:返回字符串的长度
read的作用: 读取字符串信息
r := strings.NewReader("abcdefghijklmn")
fmt.Println(r.Len()) // 输出14 初始时,未读长度等于字符串长度
var buf []byte
buf = make([]byte, 5)
readLen, err := r.Read(buf)
fmt.Println("读取到的长度:", readLen) //读取到的长度5
if err != nil {
fmt.Println("错误:", err)
}
fmt.Println(buf) //adcde
fmt.Println(r.Len()) //9 读取到了5个 剩余未读是14-5
fmt.Println(r.Size()) //14 字符串的长度
Practice
任何实现了 Read()
函数的对象都可以作为 Reader
来使用。
围绕io.Reader/Writer
,有几个常用的实现
net.Conn
,os.Stdin
,os.File
: 网络、标准输入输出、文件的流读取strings.Reader
: 把字符串抽象成Readerbytes.Reader
: 把[]byte抽象成Readerbytes.Buffer
: 把[]byte抽象成Reader和Writerbufio.Reader/Writer
: 抽象成带缓冲的流读取(比如按行读写)
我们编写一个通用的阅读器至标准输出流方法,并分别传入对象 \(os.File、net.Conn、strings.Reader\)
func readerToStdout(r io.Reader, bufSize int) {
buf := make([]byte, bufSize)
for {
n, err := r.Read(buf)
if err == io.EOF {
break
}
if err != nil {
fmt.Println(err)
break
}
if n > 0 {
fmt.Println(string(buf[:n]))
}
}
}
在\(readerToStdout\) 方法中,我们传入实现了 \(io.Reader\) 接口的对象,并规定一个每次读取数据的缓冲字节切片的大小。
需要注意的是,由于是分段读取,需要使用 \(for\) 循环,通过判断 \(io.EOF\) 退出循环,同时还需要考虑其他错误。输出至 \(os.Stdin\) 标准流时需要对字节切片进行字符串类型转换,同时字节切片应该被索引截取。\(n\)是本次读取到的字节数。
如果输出时切片不被索引截取会出现什么情况。
func fileReader() {
f, err := os.Open("book.txt")
if err != nil {
panic(err)
}
defer f.Close()
buf := make([]byte, 3)
for {
n, err := f.Read(buf)
if err == io.EOF {
break
}
if err != nil {
fmt.Println(err)
break
}
if n > 0 {
fmt.Println(buf)
}
}
}
book.txt 内容为 abcd
[97 98 99]
[100 98 99]
第一次循环缓冲切片被正常填满,而第二次由于还剩一个字节,便将这一个字节读入缓冲切片中,而后面元素未被改变。假定文件字节数很小,缓冲切片很大,那么第一次就可以读取完成,这会导致输出字节数组后面的 \(0\) 或一些奇怪的内容。
func connReader() {
conn, err := net.Dial("tcp", "example.com:80")
if err != nil {
panic(err)
}
defer conn.Close()
fmt.Fprint(conn, "GET /index.html HTTP/1.0\r\n\r\n")
readerToStdout(conn, 20)
}
这里我们通过 \(net.Dial\) 方法创建一个 \(tcp\) 连接,同时我们需要使用 \(fmt.Fprint\) 方法给特定连接发送请求。\(conn\) 实现了 \(io.Reader\) 接口,可以传入 \(readerToStdout\) 方法。
func stringsReader() {
s := strings.NewReader("very short but interesting string")
readerToStdout(s, 5)
}
func fileReader() {
f, err := os.Open("book.txt")
if err != nil {
panic(err)
}
defer f.Close()
readerToStdout(f, 3)
}
我们给定 \(string\) 对象来构造 \(strings.Reader\),并传入 \(readerToStdout\) 方法。我们使用 \(os.Open\) 打开文件,所得到的 \(File\) 对象也实现了 \(os.Reader\) 接口。
Go xmas2020 学习笔记 11、io.Reader的更多相关文章
- Ext.Net学习笔记11:Ext.Net GridPanel的用法
Ext.Net学习笔记11:Ext.Net GridPanel的用法 GridPanel是用来显示数据的表格,与ASP.NET中的GridView类似. GridPanel用法 直接看代码: < ...
- SQL反模式学习笔记11 限定列的有效值
目标:限定列的有效值,将一列的有效字段值约束在一个固定的集合中.类似于数据字典. 反模式:在列定义上指定可选值 1. 对某一列定义一个检查约束项,这个约束不允许往列中插入或者更新任何会导致约束失败的值 ...
- golang学习笔记11 golang要用jetbrain的golang这个IDE工具开发才好
golang学习笔记11 golang要用jetbrain的golang这个IDE工具开发才好 jetbrain家的全套ide都很好用,一定要dark背景风格才装B 从File-->s ...
- Spring MVC 学习笔记11 —— 后端返回json格式数据
Spring MVC 学习笔记11 -- 后端返回json格式数据 我们常常听说json数据,首先,什么是json数据,总结起来,有以下几点: 1. JSON的全称是"JavaScript ...
- Python3+Selenium3+webdriver学习笔记11(cookie处理)
#!/usr/bin/env python# -*- coding:utf-8 -*-'''Selenium3+webdriver学习笔记11(cookie处理)'''from selenium im ...
- 并发编程学习笔记(11)----FutureTask的使用及实现
1. Future的使用 Future模式解决的问题是.在实际的运用场景中,可能某一个任务执行起来非常耗时,如果我们线程一直等着该任务执行完成再去执行其他的代码,就会损耗很大的性能,而Future接口 ...
- 《C++ Primer Plus》学习笔记11
<C++ Primer Plus>学习笔记11 第17章 输入.输出和文件 <<<<<<<<<<<<<< ...
- SpringMVC:学习笔记(11)——依赖注入与@Autowired
SpringMVC:学习笔记(11)——依赖注入与@Autowired 使用@Autowired 从Spring2.5开始,它引入了一种全新的依赖注入方式,即通过@Autowired注解.这个注解允许 ...
- Spring 源码学习笔记11——Spring事务
Spring 源码学习笔记11--Spring事务 Spring事务是基于Spring Aop的扩展 AOP的知识参见<Spring 源码学习笔记10--Spring AOP> 图片参考了 ...
随机推荐
- 部署YUM源仓库与NFS共享存储服务
镜像下载.域名解析.时间同步请点击 阿里巴巴开源镜像站 一.YUM YUM (Yellow dog Updater Modified) ●基于RPM包构建的软件更新机制 ●可以自动解决依赖关系 ●所有 ...
- mongoDb入门并整合springboot
镜像下载.域名解析.时间同步请点击 阿里巴巴开源镜像站 MongoDb简介 MongoDb是一种非关系型数据库,是现在非常火热的noSQL.也被称为文档性数据库.(可存放json,xml等格式) mo ...
- [SPDK/NVMe存储技术分析]007 - 初识UIO
注: 要进一步搞清楚SSD盘对应的PCI的BAR寄存器的映射,有必要先了解一下UIO(Userspace I/O). UIO(Userspace I/O)是运行在用户空间的I/O技术.在Linux系统 ...
- SQL存储过程的学习01
虽工作多年,但是sql的存储过程一致都没怎么用过,今天来按照博客https://www.cnblogs.com/applelife/p/11016674.html来学习一下(我使用postgre sq ...
- python 多ip端口扫描器
from socket import * import threading #导入线程相关模块 import re # qianxiao996精心制作 #博客地址:https://blog.csdn. ...
- Ubuntu修改登录密码
Ubuntu 修改用户名或密码后无法登陆 解决方法 2017年09月01日 16:48:05 lee_3312 阅读数:2192 解决方法:进入recovery-mode修改登录信息 1)重启系统 ...
- 判断一文件是不是字符设备文件,如果是将其拷贝到 /dev 目录下?
#!/bin/bashread -p "Input file name: " FILENAMEif [ -c "$FILENAME" ];then cp $FI ...
- 使用postman进行post请求传递中文导致后台接收乱码的问题
1.个人猜测估计是如果header里不指明编码的话,经过tomcat服务器时会导致转换乱码信息,这样就算你在filter里配置了EncodingFilter相关的过滤器也无济于事.. 解决方法就是在h ...
- Java 中怎么创建 ByteBuffer?
byte[] bytes = new byte[10]; ByteBuffer buf = ByteBuffer.wrap(bytes);
- java-jsp-learnning
简介:JSP(全称Java Server Pages) 一种动态网页开发技术.它使用JSP标签在HTML网页中插入Java代码.标签通常以<%开头以%>结束. JSP是一种Java ser ...