打开文件并在另一个函数中创建 NewReader 后如何关闭文件?
How to close a file after opening the file and creating a NewReader in another function?
我希望 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++
}
...
我的问题是,如果我在 OpenFile() 中使用 "defer file.Close()",文件会过早关闭,所以我无法获得任何输入值。如何关闭A中的文件?
请注意 gzip.NewReader(file) 和 bzip2.NewReader(file) return 不同的界面。
gzip: func NewReader(r io.Reader) (*Reader, error) // Reader 有一个 func Close()
bzip2: func NewReader(r io.Reader) io.Reader // io.Reader 没有 func Close()
这就是我无法 return NewReader(file) 首先的原因。
谢谢!
在这种情况下,因为 bzip2.NewReader()
不是 return 和 io.ReadCloser
那么 Andy 的答案应该是可以接受的。
但是,我的原始答案针对的是一般情况:
您可能想要 return io.ReadCloser
而不是 io.Reader
- 这样该函数的使用者就可以调用 Close()
由 os.Open()
编写的 os.File
return 满足 io.ReadCloser
所以唯一要更改的是您的 OpenFile()
函数。
返回 io.ReadCloser 是惯用的,也是执行此操作的首选方法。它告诉调用者在完成 reader.
后应该调用 Close
另一个选项是return两个参数,reader和一个close函数。这就是 context.WithDeadline 和 context.WithTimeout 所做的:
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 更简单(因为您不必创建任何包装器类型),但在我看来,它在调用端有点笨拙。
正如其他人所提到的,您应该从您的函数中 return 一个 io.ReadCloser
。由于 bzip2.NewReader()
的 return 值不满足 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
}
}
我希望 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++
}
...
我的问题是,如果我在 OpenFile() 中使用 "defer file.Close()",文件会过早关闭,所以我无法获得任何输入值。如何关闭A中的文件?
请注意 gzip.NewReader(file) 和 bzip2.NewReader(file) return 不同的界面。
gzip: func NewReader(r io.Reader) (*Reader, error) // Reader 有一个 func Close()
bzip2: func NewReader(r io.Reader) io.Reader // io.Reader 没有 func Close()
这就是我无法 return NewReader(file) 首先的原因。
谢谢!
在这种情况下,因为 bzip2.NewReader()
不是 return 和 io.ReadCloser
那么 Andy 的答案应该是可以接受的。
但是,我的原始答案针对的是一般情况:
您可能想要 return io.ReadCloser
而不是 io.Reader
- 这样该函数的使用者就可以调用 Close()
由 os.Open()
编写的 os.File
return 满足 io.ReadCloser
所以唯一要更改的是您的 OpenFile()
函数。
返回 io.ReadCloser 是惯用的,也是执行此操作的首选方法。它告诉调用者在完成 reader.
后应该调用 Close另一个选项是return两个参数,reader和一个close函数。这就是 context.WithDeadline 和 context.WithTimeout 所做的:
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 更简单(因为您不必创建任何包装器类型),但在我看来,它在调用端有点笨拙。
正如其他人所提到的,您应该从您的函数中 return 一个 io.ReadCloser
。由于 bzip2.NewReader()
的 return 值不满足 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
}
}