2017-10-05 67 views
1

我想要func OpenFile()来读取gzip文件和bzip2文件。我将在稍后添加其他类型。如何在打开文件并在另一个函数中创建NewReader后关闭文件?

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  
    } 
} 

我打电话的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)(*阅读器,错误)//读取器有一个FUNC关闭()

的bzip2:FUNC NewReader(R io.Reader)io.Reader // IO。阅读器没有func关闭()

这就是我无法首先返回NewReader(文件)的原因。

谢谢!

+1

相关/可能重复(https://stackoverflow.com/questions/28279155/using-defer-with-pointers/28279237#28279237)。 – icza

回答

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 
    } 
} 
+0

没有理由对'gzip'和'file'使用'MyFileType';直接返回'gzip'和'file'即可。 – Flimzy

+1

是的。我修好了它。 –

4

在这个特定的情况下,因为bzip2.NewReader()不返回io.ReadCloser那么Andy的答案应该是可以接受的答案。

但是,我原来的答复解决了一般情况下:

您可能要返回io.ReadCloser,而不是io.Reader - 这样该函数的消费者可以致电Close()

os.File通过os.Open()返回满足io.ReadCloser所以唯一要改变的是你的OpenFile()函数的签名(返回值)。

+0

谢谢你的回答。但是,在将签名从io.Reader更改为io.ReadCloser后,我得到了这个:在返回参数中不能使用bzip2(类型io.Reader)作为类型io.ReadCloser: io.Reader没有实现io.ReadCloser(缺少Close方法) –

+0

我错过了bzip2.NewReader()不返回'io。ReadCloser'--这看起来像一个bug,但Andy的回答是解决这个问题的最常见方法。 –

3

返回io.ReadCloser是惯用的,并且是执行此操作的首选方式。它告诉调用者,当读者完成后,它预计会调用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简单(因为你没有创建任何包装类),但它是在我看来,主叫方有点笨拙。

相关问题