2017-10-05 66 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簡單(因爲你沒有創建任何包裝類),但它是在我看來,主叫方有點笨拙。

相關問題