课程地址 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. 部署YUM源仓库与NFS共享存储服务

    镜像下载.域名解析.时间同步请点击 阿里巴巴开源镜像站 一.YUM YUM (Yellow dog Updater Modified) ●基于RPM包构建的软件更新机制 ●可以自动解决依赖关系 ●所有 ...

  2. mongoDb入门并整合springboot

    镜像下载.域名解析.时间同步请点击 阿里巴巴开源镜像站 MongoDb简介 MongoDb是一种非关系型数据库,是现在非常火热的noSQL.也被称为文档性数据库.(可存放json,xml等格式) mo ...

  3. [SPDK/NVMe存储技术分析]007 - 初识UIO

    注: 要进一步搞清楚SSD盘对应的PCI的BAR寄存器的映射,有必要先了解一下UIO(Userspace I/O). UIO(Userspace I/O)是运行在用户空间的I/O技术.在Linux系统 ...

  4. SQL存储过程的学习01

    虽工作多年,但是sql的存储过程一致都没怎么用过,今天来按照博客https://www.cnblogs.com/applelife/p/11016674.html来学习一下(我使用postgre sq ...

  5. python 多ip端口扫描器

    from socket import * import threading #导入线程相关模块 import re # qianxiao996精心制作 #博客地址:https://blog.csdn. ...

  6. Ubuntu修改登录密码

    Ubuntu 修改用户名或密码后无法登陆 解决方法 2017年09月01日 16:48:05 lee_3312 阅读数:2192   解决方法:进入recovery-mode修改登录信息 1)重启系统 ...

  7. 判断一文件是不是字符设备文件,如果是将其拷贝到 /dev 目录下?

    #!/bin/bashread -p "Input file name: " FILENAMEif [ -c "$FILENAME" ];then cp $FI ...

  8. 使用postman进行post请求传递中文导致后台接收乱码的问题

    1.个人猜测估计是如果header里不指明编码的话,经过tomcat服务器时会导致转换乱码信息,这样就算你在filter里配置了EncodingFilter相关的过滤器也无济于事.. 解决方法就是在h ...

  9. Java 中怎么创建 ByteBuffer?

    byte[] bytes = new byte[10]; ByteBuffer buf = ByteBuffer.wrap(bytes);

  10. java-jsp-learnning

    简介:JSP(全称Java Server Pages) 一种动态网页开发技术.它使用JSP标签在HTML网页中插入Java代码.标签通常以<%开头以%>结束. JSP是一种Java ser ...