我希望func OpenFile()能够读取gzip文件和bzip2文件。我稍后会添加其他类型。
func OpenFile(name string) io.Reader{
file, err := os.Open(name)
if err != nil {
log.Fatal(err)
}
if(strings.Contains(name, ".gz")){
gzip, gerr := gzip.NewReader(file)
if gerr != nil {
log.Fatal(gerr)
}
return gzip
}else if(strings.Contains(name, ".bz2")){
bzip2 := bzip2.NewReader(file)
return bzip2
}else{
return file
}
}
我在另一个函数A:
中调用OpenFile() in := OpenFile(p)
for _, d := range fdb.Detect(in) {
set[d] = true
counter++
}
...
我的问题是,如果我使用"推迟file.Close()"在OpenFile()中,文件太早关闭,所以我无法获得任何输入值。如何在A中关闭文件?
请注意,gzip.NewReader(文件)和bzip2.NewReader(文件)返回不同的接口。
gzip:func NewReader(r io.Reader)(* Reader,error)// Reader有一个func Close()
bzip2:func NewReader(r io.Reader)io.Reader // io.Reader没有func关闭()
这就是我无法首先返回NewReader(文件)的原因。
谢谢!
答案 0 :(得分:4)
在这种特定情况下,由于bzip2.NewReader()
没有返回io.ReadCloser
,因此Andy的答案应该是可接受的答案。
但是,我的原始答案解决了一般情况:
您可能希望返回io.ReadCloser
而不是io.Reader
- 这样该功能的消费者可以调用Close()
os.File
返回的os.Open()
符合io.ReadCloser
,因此唯一需要更改的是OpenFile()
函数的签名(返回值)。
答案 1 :(得分:3)
返回io.ReadCloser是惯用的,并且是执行此操作的首选方法。它告诉调用者,当读者完成时,它应该调用Close。
另一种选择是返回两个参数,即reader和close函数。这就是context.WithDeadline和context.WithTimeout do:
func OpenFile(name string) (r io.Reader, close func() error) {
// ...
var file *os.File
gzip, _ := gzip.NewReader(file)
return gzip, func() error { return file.Close() }
}
这可能会使OpenFile更简单(因为你不必创建任何包装类型),但在我看来,它在调用方面有点笨拙。
答案 2 :(得分:2)
正如其他人提到的那样,你应该从你的函数中返回io.ReadCloser
。由于bzip2.NewReader()
的返回值不满足io.ReadCloser
,因此您需要创建自己的类型。
type myFileType struct {
io.Reader
io.Closer
}
func OpenFile(name string) io.ReadCloser {
file, err := os.Open(name)
if err != nil {
log.Fatal(err)
}
if strings.Contains(name, ".gz") {
gzip, gerr := gzip.NewReader(file)
if gerr != nil {
log.Fatal(gerr)
}
return gzip
} else if strings.Contains(name, ".bz2") {
bzip2 := bzip2.NewReader(file)
return myFileType{bzip2, file}
} else {
return file
}
}