golang-bufio 缓冲扫描

news2025/1/21 20:29:14

前面两篇博客,介绍了 bufio 包中的缓冲读和写(bufio.go),下面再来介绍一下缓冲扫描(scan.go)。这个扫描的是用来对缓存读的更高级封装,提供了一些更易用的方法。

缓冲扫描

Scanner 提供了一个方便的接口来读取数据,例如使用换行符分隔的文本行文件。它可以很方便的将数据转换成各种各样的 Token。Token 的规范是被类型为 SplitFuncsplit 函数定义的;默认的 split 函数会把输入转换成以去除行尾的文本行。在这个包内定义的 split 函数被用来将文件扫描成文本行,字节,UTF-8 字符和空格分隔的单词。客户端也可以提供自定义的 split 函数。

如果需要在错误处理或者超大 token 上进行更多控制,或者必须在 reader 上指向顺序扫描的程序,应该使用 bufio.Reader 替代。具体可以了解这篇博客的内容:Golang读取单行超长的文本

// Scanner provides a convenient interface for reading data such as
// a file of newline-delimited lines of text. Successive calls to
// the Scan method will step through the 'tokens' of a file, skipping
// the bytes between the tokens. The specification of a token is
// defined by a split function of type SplitFunc; the default split
// function breaks the input into lines with line termination stripped. Split
// functions are defined in this package for scanning a file into
// lines, bytes, UTF-8-encoded runes, and space-delimited words. The
// client may instead provide a custom split function.
//
// Scanning stops unrecoverably at EOF, the first I/O error, or a token too
// large to fit in the buffer. When a scan stops, the reader may have
// advanced arbitrarily far past the last token. Programs that need more
// control over error handling or large tokens, or must run sequential scans
// on a reader, should use bufio.Reader instead.
type Scanner struct {
	r            io.Reader // The reader provided by the client.
	split        SplitFunc // The function to split the tokens.
	maxTokenSize int       // Maximum size of a token; modified by tests.
	token        []byte    // Last token returned by split.
	buf          []byte    // Buffer used as argument to split.
	start        int       // First non-processed byte in buf.
	end          int       // End of data in buf.
	err          error     // Sticky error.
	empties      int       // Count of successive empty tokens.
	scanCalled   bool      // Scan has been called; buffer is in use.
	done         bool      // Scan has finished.
}

const (
	// MaxScanTokenSize is the maximum size used to buffer a token
	// unless the user provides an explicit buffer with Scanner.Buffer.
	// The actual maximum token size may be smaller as the buffer
	// may need to include, for instance, a newline.
	MaxScanTokenSize = 64 * 1024

	startBufSize = 4096 // Size of initial allocation for buffer.
)

它其实和前面的缓冲 Reader 很像,同样都是对于底层数据源的一种封装。但是它提供了一些更加方便的方法,对于缓冲 Reader 而言,它的方法通常比较低级,真正使用起来不是很方便(例如每次读取一个单词,这样就需要自己去对读取的数据做处理了)。相比之下,Scanner 提供了更为细化的方法,它会对缓冲区的内容进行切分成 token。token 可以是字节,字符,字符串或者单词。

注意:默认的缓冲区大小是 4096,最大的扫描 token 大小为:64*1024,即 64KB。

split 切分函数

我们来看一下这个包内最重要的函数的签名:

type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)

它是用来把输入转换成 token,我们不会直接使用它。只需要在创建 Scanner 时指定使用哪一个函数,然后调用 scan 即可。

bufio 包默认提供的几个 split 函数实现:

  • ScanBytes 返回每个字节作为一个 token
  • ScanRunes 返回每个字符(UTF-8)作为一个 token
  • ScanLines 返回每个文本行(移除行尾标记 \r?\n,注意这个正则表示 \r\n 或者 \n
  • ScanWords 返回每个被空格分隔的单词(只返回单词,不包括空格)

创建 Scanner 时,split 默认使用了 ScanLines 函数:

// NewScanner returns a new Scanner to read from r.
// The split function defaults to ScanLines.
func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		r:            r,
		split:        ScanLines,
		maxTokenSize: MaxScanTokenSize,
	}
}

Scan 扫描方法

// Scan advances the Scanner to the next token, which will then be
// available through the Bytes or Text method. It returns false when the
// scan stops, either by reaching the end of the input or an error.
// After Scan returns false, the Err method will return any error that
// occurred during scanning, except that if it was io.EOF, Err
// will return nil.
// Scan panics if the split function returns too many empty
// tokens without advancing the input. This is a common error mode for
// scanners.
func (s *Scanner) Scan() bool

这个函数挺复杂的,这里只介绍一下它的作用。它将前进 Scanner 到下一个 token,可以通过 Bytes 或者 Text 方法获取到这个 Token。当扫描停止时,无论是到达输入的末尾还是出现错误,它都会返回 false。在扫描返回 false 之后,可以通过 Err 方法获取扫描期间发生的错误,除非这个错误是 io.EOF,那么 Err 会返回 nil

也就是说,正常调用一次 Scan 会获取一个 token,它被存放于 Scanner 中。根据前面 Scanner 结构体中的定义,它是一个字节切片类型。这里提供了两种方式来获取它,一种是返回字节切片,另一种是字符串形式。

// Bytes returns the most recent token generated by a call to Scan.
// The underlying array may point to data that will be overwritten
// by a subsequent call to Scan. It does no allocation.
func (s *Scanner) Bytes() []byte {
	return s.token
}

// Text returns the most recent token generated by a call to Scan
// as a newly allocated string holding its bytes.
func (s *Scanner) Text() string {
	return string(s.token)
}

代码示例

所以一个普通的扫描文本的代码示例如下:

package main

import (
	"bufio"
	"fmt"
	"strings"
)

func main() {
	// 这里不使用字符串存储数据,而是使用字符串
	// 这样我可以很方便地使用字符串作为 Reader
	text := "curl 卷曲的\r\ncruel 冷酷的\n============\r\nmass 质量\nmess 混乱\n" +
		"============\nmetal 金属\r\nmental 精神的\n============\r\r\nsweep 扫除\nweep 哭泣\n" +
		"============\nwipe 擦除\r\r\nwhip 鞭打\n"

	reader := strings.NewReader(text)
	scanner := bufio.NewScanner(reader)
	// bufio.ScanLines 是默认的,所以可以不显式指定
	// scanner.Split(bufio.ScanWords)

	var line string
	for scanner.Scan() {
		line = scanner.Text()
		fmt.Print(line)
	}

}

在这里插入图片描述

这里我打印扫描到的字符串 token,但是并不换行。可以看到输出是三行了。因为我在测试数据中加入了 \r\r\n,前面介绍了扫描字符串时,只会处理 \r\n 或者 \n 的情况。那么这种情况下,它输出的就是 字符串本身\r,最后会多一个 \r 字符。使用 fmt.Println() 输出是不行的,因为它默认添加一个 \n,但是 \r\n\n 的显示效果是相同的,我是 Windows 平台

我们来看一下源码中是如何处理的,它有一个去除 \r 的函数。你看它的逻辑很简单,只是看最后一位是不是 \r,如果是的话就返回不包括最后一位的字节切片,否则返回全部字节切片。

// dropCR drops a terminal \r from the data.
func dropCR(data []byte) []byte {
	if len(data) > 0 && data[len(data)-1] == '\r' {
		return data[0 : len(data)-1]
	}
	return data
}

如果需要指定其他的分隔方式,可以将上面注释的代码放开,选择自己想要的函数。例如使用扫描单词的函数:scanner.Split(bufio.ScanWords)

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/996684.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电子技术基础(三)__第2章放大电路原理__英文简称

静态分析, 又称为直流分析, 用于求出电路的直流工作状态, 即l输入信号 。 一 . 先看几个英文符号 : 集电极及发射极间电压, 简称管压降 : 发射结电压降, 二. 接着看 加上Q点的英文简称 Q点: 放大电路的静态工作点&#…

【Spring面试】二、BeanFactory与IoC容器的加载

文章目录 Q1、BeanFactory的作用是什么?Q2、BeanDefinition的作用是什么?Q3、BeanFactory和ApplicationContext有什么区别?Q4、BeanFactory和FactoryBean有什么区别?Q5、说下Spring IoC容器的加载过程(※)Q…

《向量数据库》——向量数据库的使用场景有哪些?

向量数据库在许多应用领域都有广泛的用途,特别是那些需要存储、检索和分析向量数据的场景。以下是一些常见的向量数据库使用场景: 1、相似性搜索: 推荐系统:用于根据用户的历史行为或兴趣,搜索相似用户或物品,以提供个性化推荐。图像检索:允许用户通过图像查询相似的图像…

Leangoo领歌 -敏捷任务管理软件,任务管理更轻松更透明

​任务管理,简单易懂,就是对任务进行管理。那怎么可以更好进行任务管理呢?怎么样样可以让任务进度可视化,一目了然呢?有效的管理可以让我们事半功倍。 接下来我们看一下如何借助任务管理软件高效的做任务管理。 首先…

机器学习实战-系列教程6:SVM分类实战1(鸢尾花数据集/软间隔/线性SVM/非线性SVM/scikit-learn框架)项目实战、原理解读、代码解读

🌈🌈🌈机器学习 实战系列 总目录 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 SVM分类实战1 SVM分类实战2 支持向量机(Support Vector Machines,SVM),用于分类和…

开箱报告,Simulink Toolbox库模块使用指南(六)——S-Fuction模块(TLC)

文章目录 前言 Target Language Compiler(TLC) C MEX S-Function模块 编写TLC文件 生成代码 Tips 分析和应用 总结 前言 见《开箱报告,Simulink Toolbox库模块使用指南(一)——powergui模块》 见《开箱报告&am…

Unity中Shader抓取屏幕并实现扭曲效果实现

文章目录 前言一、屏幕抓取,在上一篇文章已经写了二、实现抓取后的屏幕扭曲实现思路:1、屏幕扭曲要借助传入 UV 贴图进行扭曲2、传入贴图后在顶点着色器的输入参数处,传入一个 float2 uv : TEXCOORD,用于之后对扭曲贴图进行采样3、…

【鲁棒电力系统状态估计】基于投影统计的电力系统状态估计的鲁棒GM估计器(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

Orangepi Zero2 全志H616(一):配置初始化和启动流程

目录 一,Orangepi简单说明 ①为什么使用全志H616 ②基本特性 ③配套操作系统支持 二,刷机和系统启动 ①准备工具 ②登录系统 ● 开发板供电 ● 登录 ● 开发板上板载LED灯测试说明 ③修改登录密码 ④网络配置 ⑤SSH登陆开发板 三&#xff…

(二十六)大数据实战——kafka集群之Kraft模式安装与部署

前言 本节内容主要介绍kafka3.0版本以后,一种新的kafka集群搭建模式看kraft,在该模式下,kafka高可用不在依赖于zookeeper,用 controller 节点代替 zookeeper,元数据保存在 controller 中,由 controller 直…

产品路线图管理,实践如何管理产品路线图和路线图规划

​什么是产品路线图? 产品路线图是一个高层次的战略计划,它描述了产品在未来一段时间可能会如何发展和壮大。 产品路线图确保整个产品团队持续关注产品的目标,帮助产品负责人把握产品的战略方向,调整产品的优先级和产品规划。 …

【实践篇】Redis最强Java客户端Redisson

文章目录 1. 前言2. Redisson基础概念2.1 数据结构和并发工具2.1.1 对Redis原生数据类型的封装和使用2.1.2 分布式锁实现和应用2.1.3 分布式集合使用方法 2.2 Redisson的高级特性2.2.1 分布式对象实现和使用2.2.2 分布式消息队列实现和使用2.2.3 分布式计数器实现和使用 3. 参考…

hadoop伪分布模式配置

1、修改/usr/local/hadoop/etc/hadoop/core-site.xml和/usr/local/hadoop/etc/hadoop/hdfs-site.xml文件 core-site.xml内容 <configuration><property><name>hadoop.tmp.dir</name><value>file:/usr/local/hadoop/tmp</value><descr…

日志平台搭建第四章:Linux安装kibana

相关链接 https://www.elastic.co/cn/downloads/kibana https://artifacts.elastic.co/downloads/kibana/kibana-7.5.1-linux-x86_64.tar.gz 官网下载可能比较慢&#xff0c;下面提供下载地址 百度云链接&#xff1a;https://pan.baidu.com/s/1d9Cqr9EwHF94op90F57bww 提取码…

《微服务架构设计模式》第二章

文章目录 微服务架构是什么软件架构是什么软件架构的定义软件架构的41视图模型为什么架构如此重要 什么是架构风格分层式架构风格六边形架构风格微服务架构风格 为应用程序定义微服务架构识别操作系统根据业务能力进行拆分根据子域进行拆分拆分指导原则单一职责原则&#xff08…

idea的GsonFormatPlus插件教程

1 安装 插件 打开idea, File—>Setting—>Plugins,搜索 GsonFormatPlus 直接安装 2 json 转化为 实体类 2.1 新建一个类 2.2 点击右键 2.4 点击Format 生成注释

聊一下酱香拿铁,瑞幸与茅台强强联手

&#xff08;点击即可收听&#xff09; 这两天&#xff0c;酱香拿铁火爆朋友圈了的 为什么唯独酱香拿铁会火成这样&#xff0c;不知道有人思考过背后逻辑&#xff1f; 难道只是因为一个新出的拿铁咖啡吗&#xff1f; 奇葩咖啡那么多为什么都没有这个一出来就爆火。 联名本身就是…

BUUCTF内涵的软件 1

使用die查看文件信息&#xff0c;没有pe64就是pe32 运行看看 使用IDA打开文件 shift F12 打开字符串窗口 可能有人猜到了上面的 DBAPP{49d3c93df25caad81232130f3d2ebfad} 可能就是flag&#xff0c;但是我们保持做题的思路来得到这个flag 因为编码问题&#xff0c;这里显…

第29节-PhotoShop基础课程-滤镜库

文章目录 前言1.滤镜库2.Camera Raw滤镜 &#xff08;用来对图片进行预处理&#xff0c;最全面的一个&#xff09;3.神经滤镜&#xff08;2022插件 需要先下载&#xff09;4.液化&#xff08;胖-> 瘦 矮->高&#xff09;5.其它滤镜1.自适应广角2.镜头矫正 把图片放正3.消…

分享配置FreeRTOSConfig.h文件因部分宏值配置不对以及相应函数未定义出现的三个错误解决方法

今天来分享一个在创建FreeRTOS时候调用官方的FreeRTOSConfig头文件时&#xff0c;因部分宏值的配置与FreeRTOS内核文件中的函数不匹配&#xff0c;导致编译时候出现了相应的错误。 于是&#xff0c;既然遇到了&#xff0c;就准备拿出来讲一下&#xff0c;让其他遇到的小伙伴也…