Go十大常见错误第9篇:使用文件名称作为函数输入
前言
问题场景
一个常见错误是把文件名作为函数参数,在函数里读取文件内容。
比如,我们要实现一个函数,用来统计指定文件里有多少空行,很多人最容易联想到的实现方式如下:
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
这种方式其实也没大问题,比较好理解。只是可扩展性不强,没有充分利用到Go语言里关于数据读写的接口(interface)类型的优势。
试想下,如果你想对一个HTTP body里的内容实现相同的逻辑,那上面的代码无法支持,要另外实现一个新的函数。
解决方案
Go语言里有2个很好的抽象接口(interface),分别是io.Reader和io.Writer。
和上面函数传参使用文件名不一样,我们可以使用io.Reader作为函数的参数类型。
因为文件、HTTP body、都实现了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")))
开源地址
文章和示例代码开源在GitHub: 。
个人网站:。
知乎:。
福利
我为大家整理了一份后端开发学习资料礼包,包含编程语言入门到进阶知识(Go、C++、Python)、后端开发技术栈、面试题等。
References
-
https://itnext.io/the-top-10-most-common-mistakes-ive-seen-in-go-projects-4b79d4f6cd65