课程地址 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: 把字符串抽象成Reader
  • bytes.Reader: 把[]byte抽象成Reader
  • bytes.Buffer: 把[]byte抽象成Reader和Writer
  • bufio.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的更多相关文章

  1. Ext.Net学习笔记11:Ext.Net GridPanel的用法

    Ext.Net学习笔记11:Ext.Net GridPanel的用法 GridPanel是用来显示数据的表格,与ASP.NET中的GridView类似. GridPanel用法 直接看代码: < ...

  2. SQL反模式学习笔记11 限定列的有效值

    目标:限定列的有效值,将一列的有效字段值约束在一个固定的集合中.类似于数据字典. 反模式:在列定义上指定可选值 1. 对某一列定义一个检查约束项,这个约束不允许往列中插入或者更新任何会导致约束失败的值 ...

  3. golang学习笔记11 golang要用jetbrain的golang这个IDE工具开发才好

    golang学习笔记11   golang要用jetbrain的golang这个IDE工具开发才好  jetbrain家的全套ide都很好用,一定要dark背景风格才装B   从File-->s ...

  4. Spring MVC 学习笔记11 —— 后端返回json格式数据

    Spring MVC 学习笔记11 -- 后端返回json格式数据 我们常常听说json数据,首先,什么是json数据,总结起来,有以下几点: 1. JSON的全称是"JavaScript ...

  5. Python3+Selenium3+webdriver学习笔记11(cookie处理)

    #!/usr/bin/env python# -*- coding:utf-8 -*-'''Selenium3+webdriver学习笔记11(cookie处理)'''from selenium im ...

  6. 并发编程学习笔记(11)----FutureTask的使用及实现

    1. Future的使用 Future模式解决的问题是.在实际的运用场景中,可能某一个任务执行起来非常耗时,如果我们线程一直等着该任务执行完成再去执行其他的代码,就会损耗很大的性能,而Future接口 ...

  7. 《C++ Primer Plus》学习笔记11

    <C++ Primer Plus>学习笔记11 第17章 输入.输出和文件 <<<<<<<<<<<<<< ...

  8. SpringMVC:学习笔记(11)——依赖注入与@Autowired

    SpringMVC:学习笔记(11)——依赖注入与@Autowired 使用@Autowired 从Spring2.5开始,它引入了一种全新的依赖注入方式,即通过@Autowired注解.这个注解允许 ...

  9. Spring 源码学习笔记11——Spring事务

    Spring 源码学习笔记11--Spring事务 Spring事务是基于Spring Aop的扩展 AOP的知识参见<Spring 源码学习笔记10--Spring AOP> 图片参考了 ...

随机推荐

  1. 2022 年最受瞩目的新特性 CSS @layer 到底是个啥?

    步入 2022,CSS 的新特性层出不穷,而最近在 CSS 圈最受瞩目的新特性,非 CSS @layer 莫属. 本文,将用最简洁的语言,快速让读者们搞懂,到底什么是 CSS @layer 新规范. ...

  2. python轻松入门——爬取豆瓣Top250时出现403报错

    关于爬虫程序的418+403报错. 1.按F12打开"开发者调试页面"如下图所示:按步骤,选中Network,找到使用的接口,获取到浏览器访问的信息. 我们需要把自己的python ...

  3. 七天接手react项目 系列 —— react 路由

    其他章节请看: 七天接手react项目 系列 react 路由 本篇首先讲解路由原理,接着以一个基础路由示例为起点讲述路由最基础的知识,然后讲解嵌套路由.路由传参,最后讲解路由组件和一般组件的区别,以 ...

  4. mtu的原理和作用

    MTU: 最大传输单元,是指一种通信协议的某一层上面所能通过的最大数据包大小,最大传输单元这个参数通常与通信接口有关. 因为协议数据单元的包头和包尾的长度是固定的,MTU越大,则一个协议数据单元的承载 ...

  5. 基于SpringBoot实现自动装配返回属性

    一:需求背景 在业务开发中经常会有这个一个场景,A(业务表)表中会记录数据的创建人,通常我们会用userId字段记录该数据的创建者,但数据的使用方会要求展示该数据的创建者姓名,故我们会关联用户表拿该用 ...

  6. 10年.NET老程序员推荐的7个开发类工具

    做.NET软件工作已经10年了,从程序员做 到高级程序员,再到技术主管,技术总监.见证了Visual Studio .NET 2003,Visul Studio 2005, Visual Studio ...

  7. Golang 包了解以及程序的执行

    Golang 包了解以及程序的执行 引言  Go 语言是使用包来组织源代码的,包(package)是多个 Go 源码的集合,是一种高级的代码复用方案.Go 语言中为我们提供了很多内置包,如 fmt.o ...

  8. python 模块和包的基础知识

    1.常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀 2.为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理.这时我们不仅 ...

  9. Eureka和zookeeper都可以提供服务注册与发现的功能,请说说两个的区别?

    Zookeeper保证了CP(C:一致性,P:分区容错性),Eureka保证了AP(A:高可用) (1)当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的信息,但不能容忍直接down ...

  10. nginx使用与配置

    一.nginx操作命令 nginx常用命令: 验证配置是否正确: nginx -t 查看Nginx的版本号:nginx -V 启动Nginx:start nginx 重新加载nginx:nginx.e ...