La bibliothèque standard de Go n'a pas de fonction destinée uniquement à vérifier si un fichier existe ou non (comme Python os.path.exists
). Quelle est la façon idiomatique de le faire?
La bibliothèque standard de Go n'a pas de fonction destinée uniquement à vérifier si un fichier existe ou non (comme Python os.path.exists
). Quelle est la façon idiomatique de le faire?
Réponses:
Pour vérifier si un fichier n'existe pas, équivalent à Python if not os.path.exists(filename)
:
if _, err := os.Stat("/path/to/whatever"); os.IsNotExist(err) {
// path/to/whatever does not exist
}
Pour vérifier si un fichier existe, équivalent à Python if os.path.exists(filename)
:
Modifié: selon les commentaires récents
if _, err := os.Stat("/path/to/whatever"); err == nil {
// path/to/whatever exists
} else if os.IsNotExist(err) {
// path/to/whatever does *not* exist
} else {
// Schrodinger: file may or may not exist. See err for details.
// Therefore, do *NOT* use !os.IsNotExist(err) to test for file existence
}
NOTEXIST
, par exemple, s'il /etc/bashrc
existe, le /etc/bashrc/foobar
retourneENOTDIR
!os.IsNotExist(err)
. Il est possible que le fichier existe mais os.Stat
échoue pour d'autres raisons (par exemple, autorisation, disque défaillant). L'utilisation err == nil
comme condition catégorise de manière incorrecte ces échecs comme «le fichier n'existe pas».
Réponse de Caleb Spare publiée dans la liste de diffusion des gonuts .
[...] Ce n'est pas vraiment nécessaire très souvent et [...] l'utilisation
os.Stat
est assez facile pour les cas où cela est nécessaire.[...] Par exemple: si vous allez ouvrir le fichier, il n'y a aucune raison de vérifier s'il existe en premier. Le fichier peut disparaître entre la vérification et l'ouverture, et de toute façon vous devrez vérifier l'
os.Open
erreur malgré tout. Il vous suffit donc d'appeleros.IsNotExist(err)
après avoir essayé d'ouvrir le fichier et de gérer sa non-existence (si cela nécessite une gestion particulière).[...] Vous n'avez pas besoin de vérifier les chemins existants (et vous ne devriez pas).
os.MkdirAll
fonctionne que les chemins existent déjà ou non. (Vous devez également vérifier l'erreur de cet appel.)Au lieu d'utiliser
os.Create
, vous devez utiliseros.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
. De cette façon, vous obtiendrez une erreur si le fichier existe déjà. De plus, cela n'a pas de condition de concurrence avec autre chose qui crée le fichier, contrairement à votre version qui vérifie l'existence au préalable.
Tiré de: https://groups.google.com/forum/#!msg/golang-nuts/Ayx-BMNdMFo/4rL8FFHr8v4J
Vous devez utiliser les fonctions os.Stat()
et os.IsNotExist()
comme dans l'exemple suivant:
// Exists reports whether the named file or directory exists.
func Exists(name string) bool {
if _, err := os.Stat(name); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}
L'exemple est extrait d' ici .
L' exemple de user11617 est incorrect; il signalera que le fichier existe même dans les cas où il n'existe pas, mais il y a eu une erreur d'une autre sorte.
La signature doit être Exists (chaîne) (bool, erreur). Et puis, en l'occurrence, les sites d'appel ne sont pas meilleurs.
Le code qu'il a écrit serait mieux:
func Exists(name string) bool {
_, err := os.Stat(name)
return !os.IsNotExist(err)
}
Mais je suggère ceci à la place:
func Exists(name string) (bool, error) {
_, err := os.Stat(name)
if os.IsNotExist(err) {
return false, nil
}
return err != nil, err
}
err != nil
au lieu de err == nil
? S'il y a une erreur, alors le fichier n'existe probablement pas?
Ce que d'autres réponses ont manqué, c'est que le chemin donné à la fonction pourrait en fait être un répertoire. La fonction suivante s'assure que le chemin est vraiment un fichier.
func fileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
Une autre chose à souligner: ce code peut toujours conduire à une condition de concurrence critique, où un autre thread ou processus supprime ou crée le fichier spécifié, tandis que la fonction fileExists est en cours d'exécution.
Si cela vous inquiète, utilisez un verrou dans vos threads, sérialisez l'accès à cette fonction ou utilisez un sémaphore interprocessus si plusieurs applications sont impliquées. Si d'autres applications sont impliquées, hors de votre contrôle, vous n'avez pas de chance, je suppose.
L'exemple de fonction:
func file_is_exists(f string) bool {
_, err := os.Stat(f)
if os.IsNotExist(err) {
return false
}
return err == nil
}
Examinons d'abord quelques aspects, à la fois la fonction fournie par le os
package de golang
ne sont pas des utilitaires mais des vérificateurs d'erreurs, ce que je veux dire par là, c'est qu'ils ne sont qu'un wrapper pour gérer les erreurs sur plusieurs plates-formes.
Donc, fondamentalement, si os.Stat
cette fonction ne donne aucune erreur, cela signifie que le fichier existe, si c'est le cas, vous devez vérifier de quel type d'erreur il s'agit, voici l'utilisation de ces deux fonctions os.IsNotExist
et os.IsExist
.
Cela peut être compris comme l' Stat
erreur de lancement de fichier car elle n'existe pas ou est-ce une erreur de lancement car elle existe et qu'il y a un problème avec elle.
Le paramètre pris par ces fonctions est de type error
, bien que vous puissiez y passer nil
, mais cela n'aurait aucun sens.
Cela souligne également le fait que IsExist is not same as !IsNotExist
ce sont deux choses différentes.
Alors maintenant, si vous voulez savoir si un fichier donné existe, je préférerais que la meilleure façon soit:
if _, err := os.Stat(path/to/file); !os.IsNotExist(err){
//TODO
}
Comme mentionné dans d'autres réponses, il est possible de construire le comportement / les erreurs requis en utilisant différents drapeaux avec os.OpenFile
. En fait, os.Create
c'est juste un raccourci sensé par défaut pour le faire:
// Create creates or truncates the named file. If the file already exists,
// it is truncated. If the file does not exist, it is created with mode 0666
// (before umask). If successful, methods on the returned File can
// be used for I/O; the associated file descriptor has mode O_RDWR.
// If there is an error, it will be of type *PathError.
func Create(name string) (*File, error) {
return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}
Vous devez combiner ces indicateurs vous-même pour obtenir le comportement qui vous intéresse:
// Flags to OpenFile wrapping those of the underlying system. Not all
// flags may be implemented on a given system.
const (
// Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
O_RDONLY int = syscall.O_RDONLY // open the file read-only.
O_WRONLY int = syscall.O_WRONLY // open the file write-only.
O_RDWR int = syscall.O_RDWR // open the file read-write.
// The remaining values may be or'ed in to control behavior.
O_APPEND int = syscall.O_APPEND // append data to the file when writing.
O_CREATE int = syscall.O_CREAT // create a new file if none exists.
O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist.
O_SYNC int = syscall.O_SYNC // open for synchronous I/O.
O_TRUNC int = syscall.O_TRUNC // truncate regular writable file when opened.
)
Selon ce que vous choisissez, vous obtiendrez différentes erreurs.
Voici un exemple où je souhaite ouvrir un fichier pour l'écriture, mais je ne tronquerai un fichier existant que si l'utilisateur a dit que c'était OK:
var f *os.File
if truncateWhenExists {
// O_TRUNC - truncate regular writable file when opened.
if f, err = os.OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644); err != nil {
log.Fatalln("failed to force-open file, err:", err)
}
} else {
// O_EXCL - used with O_CREATE, file must not exist
if f, err = os.OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0644); err != nil {
log.Fatalln("failed to open file, err:", err)
}
}