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> 图片参考了 ...
随机推荐
- 【公告】淘宝 npm 域名即将切换 && npmmirror 重构升级
镜像下载.域名解析.时间同步请点击阿里云开源镜像站 前言 本文将包括两部分内容: 淘宝 npm 域名即将停止解析 npmmirror 镜像站大重构升级 原淘宝 npm 域名即将停止解析 正如在< ...
- 实践:Linux下安装mysql8.0
镜像下载.域名解析.时间同步请点击 阿里云开源镜像站 一.下载mysql8.0安装包 1.在local创建mysql文件夹 cd /usr/local mkdir mysql cd mysql 2.使 ...
- IIS将应用程序池配置为在计划时间执行回收 (IIS 7)
将应用程序池配置为在计划时间执行回收 您可以通过以下方法执行此过程:使用用户界面 (UI).在命令行窗口中运行 Appcmd.exe 命令.直接编辑配置文件或编写 WMI 脚本. 如下只介绍用户界面U ...
- 怎么创建maven项目
1.Eclipse中用Maven创建项目 2.点击[next] 3.选maven-archetype-webapp后,next 4.填写相应的信息,Packaged是默认创建一个包,不写也可以 4右击 ...
- spring aware 的个人理解
今天学习到了spring aware ,特地百度了下这方面的知识,现在谈下我的理解. Spring的依赖注入的最大亮点就是你所有的Bean对Spring容器的存在是没有意识的.即你可以将你的容器替换成 ...
- ctfhub web 前置技能(请求方式、302跳转、Cookie)
第一题:请求方式 打开环境分析题目发现当前请求方式为GET 查看源码发现需要将请求方式改为CTFHUB就可以 使用bp抓包 发送到repeater模块修改请求方式 即可得到flag 第二题:302跳转 ...
- ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2 "No such file or directory")
/etc/init.d/mysql startmysql 没有启动参考 https://blog.csdn.net/sirobot/
- matlab二维插值--interp2与griddata
二者均是常用的二维插值方法,两者的区别是, interp2的插值数据必须是矩形域(X,Y必须分别是单调向量),即已知数据点(x,y)组成规则的矩阵,或称之为栅格,可使用meshgid生成. gridd ...
- 13_奈奎斯特稳定性判据_Nyquist Stability Criterion_Part 1
A曲线内有4个极点两个零点,则B曲线绕(0,0)逆时针两圈 A曲线是nyqyict contour中的曲线,P是A曲线内的()极点个数,Z是()极点个数,N是曲线B逆时针围绕(-1,0)的圈数 没过( ...
- DRF 视图组件
目录 DRF 视图组件 视图组件大纲 两个视图基本类 五个扩展类 九个子类视图 视图集 常用视图集父类 魔法类 一览表 DRF中视图的"七十二变" 第一层:基于APIview的五个 ...