Pourquoi la question "How to check if a file exists in Go?" est devenue virale
\n\nSi vous avez déjà écrit un programme en Go qui manipule des fichiers, vous avez sûrement rencontré cette question. Sur Stack Overflow, elle a recueilli des centaines de milliers de vues, et en dessous, des dizaines de commentaires avec des débats animés. Rien de compliqué, semble-t-il ? En Python, il y a os.path.exists(), en PHP, file_exists(), et en Go ?
Le problème est qu'il n'existe pas de fonction intégrée FileExists() en Go. Et ce n'est pas un hasard. Les développeurs du langage ont délibérément évité cette approche, car vérifier l'existence d'un fichier est une condition de concurrence (race condition) à l'état pur. Pendant que vous vérifiez, le fichier peut être supprimé ou créé par une autre goroutine. C'est pourquoi en Go, on essaie simplement d'ouvrir le fichier et de gérer l'erreur.
Mais comment faire correctement ? Comment ne pas perdre en performance et éviter les bugs cachés ? Aujourd'hui, nous allons analyser la vérification de l'existence d'un fichier en Go de A à Z : des exemples de base aux solutions de production.
\n\nPremière approche : os.Stat et os.IsNotExist (classique de Stack Overflow)
\n\nLa réponse la plus populaire sur Stack Overflow ressemble à ceci :
\n\npackage main\n\nimport (\n "fmt"\n "os"\n)\n\nfunc fileExists(filename string) bool {\n info, err := os.Stat(filename)\n if os.IsNotExist(err) {\n return false\n }\n return err == nil && !info.IsDir()\n}\n\nfunc main() {\n if fileExists("example.txt") {\n fmt.Println("Le fichier existe")\n } else {\n fmt.Println("Fichier non trouvé")\n }\n}\n\n\nCe code utilise os.Stat(), qui retourne les informations sur un fichier. Si le fichier n'existe pas, la fonction retourne une erreur, et nous la vérifions avec os.IsNotExist(). De plus, nous vérifions qu'il ne s'agit pas d'un répertoire — car os.Stat() fonctionne aussi sur un dossier.
Avantages : simplicité, lisibilité, minimum de dépendances.
\n\nInconvénients : condition de concurrence potentielle entre la vérification et l'ouverture du fichier. Si vous vérifiez l'existence puis ouvrez le fichier, le fichier peut disparaître entre ces opérations. En Go, on essaie directement d'ouvrir le fichier et de gérer l'erreur.
\n\nPerformance de os.Stat
\n\nL'appel à os.Stat() est un appel système. Sous Linux, il s'agit de stat() ou lstat(). C'est rapide, mais pas instantané. En moyenne, un appel prend 1 à 5 microsecondes sur un SSD. Si vous vérifiez des centaines de milliers de fichiers, cela peut devenir un goulot d'étranglement.
Optimisation : si vous devez vérifier l'existence de nombreux fichiers, envisagez la mise en cache des résultats ou l'utilisation de filepath.Glob() avec un masque.
Deuxième approche : ouverture du fichier et gestion des erreurs (Go Way)
\n\nUne manière plus idiomatique est d'essayer d'ouvrir le fichier directement. Cela résout le problème de race condition :
\n\npackage main\n\nimport (\n "fmt"\n "os"\n)\n\nfunc openFile(filename string) (bool, error) {\n file, err := os.Open(filename)\n if err != nil {\n if os.IsNotExist(err) {\n return false, nil\n }\n return false, err // Autre erreur : pas de droits, lien cassé, etc.\n }\n defer file.Close()\n return true, nil\n}\n\nfunc main() {\n exists, err := openFile("example.txt")\n if err != nil {\n fmt.Printf("Erreur : %v\\n", err)\n return\n }\n if exists {\n fmt.Println("Le fichier existe et est accessible")\n }\n}\n\n\nCe code ouvre immédiatement le fichier. Si une erreur survient, nous vérifions s'il s'agit bien de l'erreur "fichier non trouvé". Toute autre erreur (par exemple, droits insuffisants) est remontée — cela offre plus de contrôle.
\n\nAvantages : atomicité de l'opération, gestion correcte des erreurs, absence de race condition.
\n\nInconvénients : nous ouvrons le fichier, même si nous avons seulement besoin de vérifier son existence. Pour les gros fichiers, cela peut être coûteux