2016-12-18 1 views
3

La documentation io.Reader de Go indique qu'un Read() peut renvoyer une valeur non nulle n et une io.EOF en même temps. Malheureusement, la méthode Read() d'un File ne fait pas cela.Comment tester EOF sur io.Reader dans Go?

Lorsque l'EOF est atteint et que certains octets peuvent encore être lus, la méthode Read du fichier renvoie une erreur non nulle n et nil. Ce n'est que lorsque nous essayons de lire quand déjà à la fin du fichier que nous obtenons zéro n et io.EOF comme erreur.

Je n'ai pas trouvé de méthode simple pour tester si EOF est atteint sans essayer de lire les données du fichier. Si nous effectuons un Read() avec un tampon de 0 octet, nous obtenons zéro erreur n et nil bien que nous soyons à la fin du fichier.

Pour éviter cette dernière lecture, la seule solution que j'ai trouvée est de garder une trace moi-même du nombre d'octets restant à lire dans le fichier. Y a-t-il une solution plus simple?

+1

pas AFAIK. vous pouvez peut-être utiliser seek ou quelque chose pour obtenir la position actuelle mais comme il faudra plus de syscalls, il sera plus lent. –

+3

Pourquoi ne pas faire un type qui _always_ lit quelques octets et renvoie EOF? – JimB

+0

@JimB C'est un truc incroyable. Je vais adopter cette solution. Mais au lieu de lire à l'avance, je garderai une trace des octets restants à lire pour que je puisse retourner io.EOF juste à temps. La raison pour laquelle je l'aime le plus est que je n'ai pas besoin de modifier le code n'importe où dans mon programme. J'ai seulement besoin de fournir un wrapper à File. Si vous écrivez une bonne réponse avec les deux options, je vous l'accorde. – chmike

Répondre

1

Vous pouvez créer un nouveau type, qui conserve le nombre d'octets lus jusqu'à présent. Ensuite, au moment de la vérification EOF, vous pouvez comparer le nombre attendu d'octets lus avec le nombre réel d'octets lus. Voici un exemple d'implémentation. Le eofReader garde la trace du nombre d'octets lus et il se compare à la taille du fichier, au cas où le type sous-jacent est un fichier:

package main 

// ... imports 

// eofReader can be checked for EOF, without a Read. 
type eofReader struct { 
    r  io.Reader 
    count uint64 
} 

// AtEOF returns true, if the number of bytes read equals the file size. 
func (r *eofReader) AtEOF() (bool, error) { 
    f, ok := r.r.(*os.File) 
    if !ok { 
     return false, nil 
    } 
    fi, err := f.Stat() 
    if err != nil { 
     return false, err 
    } 
    return r.Count() == uint64(fi.Size()), nil 
} 

// Read reads and counts. 
func (r *eofReader) Read(buf []byte) (int, error) { 
    n, err := r.r.Read(buf) 
    atomic.AddUint64(&r.count, uint64(n)) 
    return n, err 
} 

// Count returns the count. 
func (r *eofReader) Count() uint64 { 
    return atomic.LoadUint64(&r.count) 
} 

Vous pouvez utiliser ce type en enveloppant tout lecteur dans un eofReader:

func main() { 
    f, err := os.Open("main.go") 
    if err != nil { 
     log.Fatal(err) 
    } 

    r := &eofReader{r: f} 
    log.Println(r.AtEOF()) 

    if _, err = ioutil.ReadAll(r); err != nil { 
     log.Fatal(err) 
    } 

    log.Println(r.AtEOF()) 
} 

// 2016/12/19 03:49:35 false <nil> 
// 2016/12/19 03:49:35 true <nil> 

Code comme gist.