golang eof 错误_Golang 最细节篇之 — Reader 和 ReaderAt 的区别

本文探讨了Golang中Reader和ReaderAt接口的区别,指出Read接口允许在数据未准备好时返回部分数据,而ReadAt接口则不允许。文章强调在封装接口时必须保持语义的一致性,以确保数据的正确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

6a7927c006c83015c9ab42dacef0c7b5.png

大纲

5651b9056f6a79f608ba3d063beb0f38.png
  • Golang 关于 Read 的两个接口定义

  • C 语言 libc 库关于 read 的接口

  • Golang 里 Read 和 ReadAt 的区别

  • 那么,会出什么样的坑呢?

  • 总结

6a7927c006c83015c9ab42dacef0c7b5.png

概述

5651b9056f6a79f608ba3d063beb0f38.png

分享一个细节语义问题引发的思考。关于 Golang Read,ReadAt 这两个接口,不知道大家有没有仔细品过这两个接口的区别。golang 里面有两个关于 Read 的 interface ,就是 Reader 和 ReaderAt ,这两个接口的定义在标准库 io 的 io.go 文件中,如下:

6a7927c006c83015c9ab42dacef0c7b5.png

Golang 关于 Read 的两个接口定义

5651b9056f6a79f608ba3d063beb0f38.png

Reader interface 定义

type Reader interface {
    
 Read(p []byte) (n int, err error)
}

ReaderAt interface 定义

type ReaderAt interface {
    
 ReadAt(p []byte, off int64) (n int, err error)
}

从 interface 接口来看,我们看到的区别是:

  • Reader 传入参数只有一个 p( 用来装读到的数据的 buffer ),返回参数是 n(读了多少数据),err(返回的错误码);
  • ReaderAt 传入参数是两个,一个 ( 装读到的数据 buffer ),off(读取的偏移位置),返回值 n(表示读到了多少数据),err(错误码);

也就是说,Read 读数据的时候只需要给一个 buff

### 抽象语法树(AST)在 Go 语言中的应用 抽象语法树(Abstract Syntax Tree,简称 AST),是一种源代码语法结构的抽象表示方式[^1]。对于编程语言而言,AST 是编译过程中的重要中间产物之一,在此过程中,原始代码被解析成一种更易于处理的形式。 #### 使用 `go/ast` 包创建操作 AST Go 提供了一个名为 `go/ast` 的标准库包专门用来生成操作 AST 结构[^2]。这个包包含了多种类型定义以及辅助函数,允许开发者轻松地构建、遍历甚至修改程序的 AST 表达形式。下面是一个简单的例子展示了如何利用 `go/parser` `go/ast` 来读取并打印给定 Go 文件的内容: ```go package main import ( "fmt" "go/parser" "go/token" "os" ) func main() { fset := token.NewFileSet() file, err := parser.ParseFile(fset, "example.go", nil, 0) if err != nil { fmt.Fprintf(os.Stderr, "parsing file: %v\n", err) os.Exit(1) } ast.Print(fset, file) } ``` 这段代码首先初始化一个新的文件集 (`token.FileSet`) 并调用 `parser.ParseFile()` 函数来加载指定路径下的 Go 源码文件到内存中作为 AST 节点对象;后通过 `ast.Print()` 方法输出整个 AST 到控制台以便查看其内部结构。 #### 修改现有 AST 实现自定义逻辑 当涉及到对已有的 AST 进行更改时,则可能需要用到更多高级特性,比如访问者模式或是递归下降解析器等技术手段。这里给出一个基于命令行工具 go generate 自动化脚本的例子,其中涉及到了对特定格式注释行(`//go:generate ...`)的操作[^3]: ```go type Generator struct { r io.Reader } func (g *Generator) run() (ok bool) { input := bufio.NewReader(g.r) for { var buf []byte buf, err = input.ReadSlice('\n') if err != nil { if err == io.EOF && isGoGenerate(buf) { err = io.ErrUnexpectedEOF } break } if !isGoGenerate(buf) { continue } g.setEnv() words := g.split(string(buf)) g.exec(words) } return true } ``` 上述片段是从官方文档简化而来的一个版本,实际应用场景可能会更加复杂一些。它实现了基本的功能——识别特殊的注解语句,并据此触发相应的动作。 #### 加载外部依赖项的信息 有时候还需要获取关于导入模块的具体细节,这可以通过调整 swag 中使用的 loader 工具实现[^4]。例如,要查找某个包名对应的实际名称,可以这样做: ```go func (pkgDefs *PackagesDefinitions) parseImportName(importPath string) string { conf := loader.Config{ ParserMode: goparser.PackageClauseOnly, } conf.Import(importPath) loaderProgram, err := conf.Load() if err != nil { return "" } for _, info := range loaderProgram.AllPackages { pkgPath := strings.TrimPrefix(info.Pkg.Path(), "vendor/") if pkgPath == importPath { return info.Pkg.Name() } } return "" } ``` 这种方法能够有效地帮助理解项目间的相互关系及其各自的命名空间布局情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值