如何在打开文件并在另一个函数中创建NewReader后关闭文件?

时间:2017-10-05 15:34:04

标签: file go io gzip bzip2

我希望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(文件)的原因。

谢谢!

3 个答案:

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