Go十大常见错误第9篇(使用文件名称作为函数输入)
前言
这是Go十大常见错误系列的第9篇:使用文件名称作为函数输入。素材来源于Go布道者,现Docker公司资深工程师Teiva Harsanyi。
本文涉及的源代码全部开源在:Go十大常见错误源代码,欢迎大家关注公众号,及时获取本系列最新更新。
问题场景
一个常见错误是把文件名作为函数参数,在函数里读取文件内容。
比如,我们要实现一个函数,用来统计指定文件里有多少空行,很多人最容易联想到的实现方式如下:
func count(filename string) (int, error) {
file, err := os.Open(filename)
if err != nil {
return 0, errors.Wrapf(err, "unable to open %s", filename)
}
defer file.Close()scanner := bufio.NewScanner(file)
count := 0
for scanner.Scan() {
if scanner.Text() == "" {
count++
}
}
return count, nil
}
这段代码逻辑很简单:
- 文件名作为函数入参
- 函数里读取文件每一行数据,判断是否为空行,如果是空行就计数+1
试想下,如果你想对一个HTTP body里的内容实现相同的逻辑,那上面的代码无法支持,要另外实现一个新的函数。
解决方案 Go语言里有2个很好的抽象接口(interface),分别是
io.Reader
和io.Writer
。和上面函数传参使用文件名不一样,我们可以使用
io.Reader
作为函数的参数类型。因为文件、HTTP body、bytes.Buffer都实现了
io.Reader
,所以- 使用
io.Reader
作为函数参数可以兼容不同类型的数据源。 - 不同的数据源,可以统一使用
io.Reader
类型里的Read
方法来读取数据。
bufio.Reader
和其ReadLine
方法,代码如下所示:func count(reader *bufio.Reader) (int, error) {
count := 0
for {
line, _, err := reader.ReadLine()
if err != nil {
switch err {
default:
return 0, errors.Wrapf(err, "unable to read")
case io.EOF:
return count, nil
}
}
if len(line) == 0 {
count++
}
}
}
err为
io.EOF
,就表示读到了空行。EOF is the error returned by Read when no more input is available. (Read must return EOF itself, not an error wrapping EOF, because callers will test for EOF using ==.) Functions should return EOF only to signal a graceful end of input. If the EOF occurs unexpectedly in a structured data stream, the appropriate error is either ErrUnexpectedEOF or some other error giving more detail.有了上面的
count
函数,我们就可以使用如下的方式打开文件,计算文件里空行的数量。file, err := os.Open(filename)
if err != nil {
return errors.Wrapf(err, "unable to open %s", filename)
}
defer file.Close()
count, err := count(bufio.NewReader(file))
这种实现方式可以让我们在计算逻辑里不需要关心真正的数据来源。同时,也可以方便我们做单元测试。
比如下面的例子,我们直接把字符串作为输入,来测试上面实现的
count
函数。count, err := count(bufio.NewReader(strings.NewReader("input")))
推荐阅读
- Go十大常见错误第1篇:未知枚举值
- Go十大常见错误第2篇:benchmark性能测试的坑
- Go十大常见错误第3篇:go指针的性能问题和内存逃逸
- Go十大常见错误第4篇:break操作的注意事项
- Go十大常见错误第5篇:Go语言Error管理
- Go十大常见错误第6篇:slice初始化常犯的错误
- Go十大常见错误第7篇:不使用-race选项做并发竞争检测
- Go十大常见错误第8篇:并发编程中Context使用常见错误
- Go面试题系列,看看你会几题?
公众号:coding进阶。关注公众号可以获取最新Go面试题和技术栈。
个人网站:Jincheng's Blog。
知乎:无忌。
福利 我为大家整理了一份后端开发学习资料礼包,包含编程语言入门到进阶知识(Go、C++、Python)、后端开发技术栈、面试题等。
【Go十大常见错误第9篇(使用文件名称作为函数输入)】关注公众号「coding进阶」,发送消息 backend 领取资料礼包,这份资料会不定期更新,加入我觉得有价值的资料。还可以发送消息「进群」,和同行一起交流学习,答疑解惑。
References
- https://itnext.io/the-top-10-...
推荐阅读
- 黑客技巧|nmap在kali的使用方法和常见命令(入门)
- Go中的错误和异常处理最佳实践
- php程序员转go常犯的错误-------sql中in功能的使用
- 杭电计算机考研|数据库面试常见问题
- 推荐系统实战|推荐系统实战1——什么是推荐系统与常见的推荐系统评价指标
- 前端面试常见js手写题记录(含答案)
- Go十大常见错误第8篇(并发编程中Context使用常见错误)
- 排序算法整理C++(初赛)
- go语言学习(3)--分支和循环
- 安装cocopods时出现错误