Fichiers et répertoires en R : guide reproductible

Lister, trouver, créer, copier, déplacer et supprimer des fichiers de façon portable — avec base R et le package fs moderne, plus here() pour des chemins relatifs au projet

Programming

Une référence pratique pour travailler avec les fichiers et les répertoires en R : construire et manipuler des chemins, lister et trouver des fichiers, vérifier l’existence et les infos de fichier, et créer, copier, déplacer, renommer et supprimer fichiers et dossiers — le tout en base R (file.exists(), dir.create(), list.files(), file.path(), …) et avec le package fs moderne. L’habitude qui compte le plus, ce sont les chemins portables : jamais de setwd(), jamais de chemin absolu codé en dur — utilisez here::here() et file.path() pour que votre script tourne aussi sur la machine de quelqu’un d’autre.

Auteur·rice
Date de publication

18 juillet 2026

Modifié

18 juillet 2026

AstuceCe que vous allez apprendre
  • Construire des chemins portables — pourquoi il ne faut jamais appeler setwd() ni coder en dur un chemin absolu, et quoi faire à la place (here::here() + file.path()).
  • Lister et trouver fichiers et dossiers — par motif, récursivement, avec list.files() / list.dirs().
  • Vérifier ce qui existe et lire les infos de fichierfile.exists(), dir.exists(), file.size(), file.info().
  • Créer, copier, déplacer, renommer et supprimer en toute sécurité fichiers et répertoires.
  • Les mêmes opérations avec le package fs moderne — une alternative cohérente et multi-plateforme aux fonctions de fichier de base R — avec un aide-mémoire base R ↔︎ fs.

Lire et écrire des données en R suppose presque toujours de toucher au système de fichiers : retrouver le CSV enregistré hier, créer un dossier de sortie, faire le ménage dans un répertoire de résultats. R fournit tout ce qu’il faut en natif — plus un package moderne, fs, qui gomme les aspérités de base R.

L’idée la plus importante n’est pourtant pas une fonction — c’est une habitude. Un chemin peut être absolu (/Users/you/project/data.csv — écrit depuis la racine du système de fichiers) ou relatif (data/raw/survey.csv — interprété à partir de votre répertoire de travail courant, le dossier que R considère comme « ici »). Un chemin relatif au projet est un chemin relatif ancré au dossier racine de votre projet plutôt qu’à l’endroit où R se trouve par hasard. Réussir cet ancrage, c’est toute la différence entre un script qui tourne partout et un script qui ne tourne que sur la machine qui l’a écrit. C’est par là que nous commençons.

Construire des chemins de façon portable

Le raccourci tentant consiste à définir le répertoire de travail en tête d’un script :

setwd("/Users/me/analysis/project")   # DON'T — see below
read.csv("data/raw/survey.csv")

Cela fonctionne exactement une fois — sur votre machine, dans ce dossier, aujourd’hui. Dès qu’un collaborateur ouvre le script, ou que vous déplacez le projet, ou qu’il s’exécute en CI, ce chemin absolu est faux. Comme le formule sans détour le guide project-oriented workflow du tidyverse : « Les chances que la commande setwd() produise l’effet recherché — faire fonctionner les chemins de fichiers — pour quiconque d’autre que son auteur sont de 0 %. »

Deux outils règlent le problème. D’abord, file.path() assemble les morceaux de chemin avec le bon séparateur selon le système d’exploitation sur lequel vous êtes (/ sur macOS/Linux, \ sur Windows) — pour que vous n’ayez jamais à coder un séparateur en dur :

# Joins with the right separator for the current OS
file.path("data", "raw", "survey.csv")
[1] "data/raw/survey.csv"

Ensuite, le package here résout les chemins à partir de votre racine de projet — le dossier contenant votre .Rproj ou .git. here::here() renvoie le même emplacement logique sur chaque machine, car il trouve la racine à l’exécution au lieu de se fier au répertoire courant. La documentation du package here le dit clairement : « Le package here crée des chemins relatifs au répertoire de plus haut niveau. »

library(here)
# here() finds the project root, then builds a path down from it.
# The result is resolved for YOUR machine, but the CODE is identical
# on everyone's machine — that's the point.
here("data", "raw", "survey.csv")
#> "/Users/you/analysis/project/data/raw/survey.csv"

Utilisez here::here() pour les chemins à l’intérieur de votre projet, et file.path() (ou fs::path(), plus bas) pour assembler des chemins de manière générale. Aucun ne dépend de setwd(), si bien que votre script survit à un déplacement, à un partage ou à une exécution par une machine.

NoteVérifiez ce que la machine vous donne

Quand vous collez un extrait de code qui manipule des chemins — venu d’un assistant IA, de StackOverflow ou d’un vieux tutoriel — l’anti-pattern révélateur est un setwd("/Users/…") nu ou un chemin absolu codé en dur. Il tourne sur la machine de l’auteur et nulle part ailleurs. Avant de lui faire confiance, posez une seule question : cela se résoudrait-il sur le portable d’un collaborateur, ou en CI ? S’il appelle setwd() ou code un chemin absolu en dur, remplacez-le par here::here() et file.path(). Ce contrôle — confronter l’extrait à l’hypothèse de reproductibilité, et pas seulement à « est-ce que ça tourne pour moi là, maintenant » — c’est le jugement qu’une réponse générée ne fera pas à votre place.

Pour les exemples exécutables ci-dessous, nous travaillerons dans un dossier de travail jetable, à l’intérieur du répertoire temporaire propre à la session R (tempdir()), pour que rien ici ne touche à vos vrais fichiers :

# A disposable scratch folder — unique to this R session, auto-cleaned on exit.
scratch <- file.path(tempdir(), "files-demo")
unlink(scratch, recursive = TRUE)   # start from a clean slate
dir.create(scratch)

# Seed it with a few files to work with.
writeLines("a,b\n1,2", file.path(scratch, "data.csv"))
writeLines("notes",    file.path(scratch, "README.md"))
writeLines("x",        file.path(scratch, "script.R"))

list.files(scratch)
[1] "data.csv"  "README.md" "script.R" 

list.files() ne renvoie que les noms de fichiers (pas le chemin temporaire complet), ce qui est exactement ce que nous voulons afficher — reproductible, et sans aucun emplacement propre à une machine.

Lister et trouver des fichiers

list.files() (que l’on peut aussi écrire dir()) liste les entrées d’un dossier. Donnez-lui un pattern — une expression régulière — pour filtrer, et recursive = TRUE pour descendre dans les sous-dossiers. La documentation de base R documente les deux arguments.

# Only the CSV files (pattern is a regular expression)
list.files(scratch, pattern = "\\.csv$")
[1] "data.csv"
# Add a subfolder, then list everything recursively.
dir.create(file.path(scratch, "raw"))
writeLines("y", file.path(scratch, "raw", "extra.csv"))

list.files(scratch, recursive = TRUE)
[1] "data.csv"      "raw/extra.csv" "README.md"     "script.R"     

Le listage récursif renvoie des chemins relatifs au dossier interrogé (raw/extra.csv), ce qui garde la sortie portable. Pour ne lister que les répertoires, utilisez list.dirs() :

# full.names = FALSE keeps the names relative and readable
list.dirs(scratch, full.names = FALSE, recursive = TRUE)
[1] ""    "raw"

(La chaîne vide "" désigne le dossier de premier niveau lui-même.) L’équivalent moderne est fs::dir_ls(), qui renvoie un vecteur de caractères tidy et accepte un filtre glob (comme *.csv) ou regexp :

library(fs)
dir_ls(scratch, recurse = TRUE, glob = "*.csv")

Vérifier l’existence et les infos de fichier

Avant de lire ou d’écraser quoi que ce soit, vérifiez que c’est bien là. file.exists() et dir.exists() renvoient de simples booléens — parfaits pour une garde if (). Voir les pages d’aide base R file.exists et dir.exists.

file.exists(file.path(scratch, "data.csv"))    # is the file there?
[1] TRUE
dir.exists(file.path(scratch, "raw"))          # is the folder there?
[1] TRUE
file.exists(file.path(scratch, "missing.txt")) # a file that isn't
[1] FALSE

Pour la taille et les métadonnées, file.size() renvoie des octets, et file.info() renvoie un data frame complet de métadonnées (taille, date de modification, permissions, et si c’est un répertoire) :

file.size(file.path(scratch, "data.csv"))   # bytes
[1] 8
# file.info() returns a metadata data frame; pull the column you need
file.info(file.path(scratch, "data.csv"))$size   # bytes
file.info(file.path(scratch, "data.csv"))$mtime  # last modified
file.info(file.path(scratch, "data.csv"))$isdir  # FALSE for a file

Les versions fs sont fs::file_exists(), fs::dir_exists() et fs::file_info() — même idée, vectorisées et nommées de façon cohérente.

Créer des fichiers et des répertoires

dir.create() crée un dossier ; passez recursive = TRUE pour créer d’un seul appel tous les dossiers parents manquants (sans cette option, un parent manquant est une erreur). file.create() crée un fichier vide.

# Make nested folders in one call — parents are created as needed
nested <- file.path(scratch, "results", "figures")
dir.create(nested, recursive = TRUE)
dir.exists(nested)
[1] TRUE
# Make an empty file
file.create(file.path(scratch, "log.txt"))
[1] TRUE
file.exists(file.path(scratch, "log.txt"))
[1] TRUE

L’équivalent fs, fs::dir_create(), est récursif par défaut (aucune option nécessaire) et n’avertit pas si le dossier existe déjà — l’un des petits gains de cohérence qui rendent fs agréable :

library(fs)
dir_create(path(scratch, "results", "figures"))  # parents made automatically
file_create(path(scratch, "log.txt"))

Copier, déplacer et renommer

file.copy() duplique un fichier ; file.rename() à la fois renomme et déplace (il n’y a pas de « déplacement » distinct en base R — renommer vers un autre dossier revient à déplacer). Les deux renvoient TRUE en cas de succès.

# Copy data.csv to a backup name
file.copy(file.path(scratch, "data.csv"),
          file.path(scratch, "data-backup.csv"))
[1] TRUE
# Rename (= move) script.R to analysis.R
file.rename(file.path(scratch, "script.R"),
            file.path(scratch, "analysis.R"))
[1] TRUE
list.files(scratch, pattern = "\\.(csv|R)$")
[1] "analysis.R"      "data-backup.csv" "data.csv"       

fs sépare ces opérations en verbes clairement nommés — fs::file_copy(), fs::file_move() — pour que l’intention se lise directement dans le code :

library(fs)
file_copy(path(scratch, "data.csv"), path(scratch, "data-backup.csv"))
file_move(path(scratch, "script.R"), path(scratch, "analysis.R"))

Supprimer en toute sécurité

La suppression est la seule opération sans retour en arrière, alors maniez-la avec précaution. file.remove() supprime des fichiers. unlink() supprime des fichiers ou des dossiers — mais pour un dossier vous devez passer recursive = TRUE, sinon rien ne se passe. La documentation de unlink() est explicite : « Si recursive = FALSE, les répertoires ne sont pas supprimés, pas même les vides. »

# Remove a single file
file.remove(file.path(scratch, "log.txt"))
[1] TRUE
file.exists(file.path(scratch, "log.txt"))     # gone
[1] FALSE
# Remove a whole folder tree — recursive = TRUE is required for directories
unlink(file.path(scratch, "results"), recursive = TRUE)
dir.exists(file.path(scratch, "results"))       # gone
[1] FALSE

Comme il n’y a pas de corbeille ici, vérifiez avant de supprimer — protégez l’appel avec file.exists() / dir.exists(), et ne pointez jamais unlink(recursive = TRUE) vers un chemin que vous avez construit par concaténation de chaînes sans l’avoir d’abord inspecté. Les verbes fs, fs::file_delete() et fs::dir_delete(), disent d’eux-mêmes lequel vous vouliez.

base R et fs : une correspondance côte à côte

Les fonctions de fichier de base R se sont accumulées au fil des décennies, si bien que leurs noms sont incohérents (file.exists() mais dir.create() ; unlink() pour « supprimer »). fs vous offre une interface unique, cohérente, multi-plateforme et vectorisée — la documentation de fs la décrit comme « une interface multi-plateforme et uniforme aux opérations sur le système de fichiers. » Chaque fonction fs prend et renvoie des vecteurs de caractères fs_path tidy, utilise / sur tous les OS, et nomme les choses de façon prévisible (dir_* pour les répertoires, file_* pour les fichiers, path_* pour les chaînes de chemin). Voici la traduction :

Tâche Base R fs
Construire un chemin file.path("a", "b") fs::path("a", "b")
Chemin relatif au projet here::here("a", "b") here::here("a", "b")
Lister les fichiers list.files(dir, pattern=) fs::dir_ls(dir, glob=)
Lister les répertoires list.dirs(dir) fs::dir_ls(dir, type="directory")
Le fichier existe ? file.exists(p) fs::file_exists(p)
Le répertoire existe ? dir.exists(p) fs::dir_exists(p)
Taille / infos de fichier file.size(p) / file.info(p) fs::file_info(p)
Créer un répertoire dir.create(p, recursive=TRUE) fs::dir_create(p)
Créer un fichier vide file.create(p) fs::file_create(p)
Copier un fichier file.copy(from, to) fs::file_copy(from, to)
Déplacer / renommer file.rename(from, to) fs::file_move(from, to)
Supprimer un fichier file.remove(p) / unlink(p) fs::file_delete(p)
Supprimer un répertoire unlink(p, recursive=TRUE) fs::dir_delete(p)
Extension de fichier tools::file_ext(p) fs::path_ext(p)

Base R est toujours disponible sans aucune dépendance — privilégiez-le dans les packages et les scripts que vous voulez garder légers en dépendances. Tournez-vous vers fs quand vous voulez la cohérence, le comportement vectorisé, et des chemins qui se ressemblent sur tous les systèmes d’exploitation.

Problèmes courants

setwd() marche pour vous mais casse pour tous les autres. Un chemin absolu ou un appel à setwd() grave l’organisation des dossiers de votre machine dans le script. Supprimez-le. Ancrez les chemins à la racine du projet avec here::here() et construisez-les avec file.path() (ou fs::path()), pour que le code soit identique sur chaque machine même si le chemin résolu diffère.

Les chemins à antislash échouent sur d’autres systèmes d’exploitation. Coder en dur "data\\raw\\file.csv" (Windows) casse sur macOS/Linux, et vice-versa. Ne tapez jamais le séparateur vous-même — laissez file.path() ou fs::path() insérer le bon pour le système d’exploitation courant.

unlink() « n’a pas supprimé » mon dossier. Par défaut, unlink() refuse de supprimer les répertoires — vous devez passer recursive = TRUE. (file.remove() ne supprime jamais que des fichiers, il échoue donc silencieusement sur un dossier lui aussi.) Pour un répertoire, utilisez unlink(path, recursive = TRUE) ou le plus clair fs::dir_delete().

Foire aux questions

Utilisez list.files("path/to/folder"), qui renvoie les noms de fichiers de ce dossier. Ajoutez pattern = "\\.csv$" pour filtrer avec une expression régulière, recursive = TRUE pour descendre dans les sous-dossiers, et full.names = TRUE si vous voulez des chemins complets plutôt que de simples noms. L’équivalent moderne est fs::dir_ls("path/to/folder"), qui accepte un filtre glob = "*.csv" et renvoie un vecteur de chemins tidy.

Utilisez dir.create("path/to/folder"). Pour créer dans le même appel tous les dossiers parents manquants, ajoutez recursive = TRUE — sinon un parent manquant est une erreur. fs::dir_create("path/to/folder") fait la même chose, est récursif par défaut, et n’avertit pas si le dossier existe déjà.

Utilisez file.exists("path/to/file"), qui renvoie TRUE ou FALSE — idéal pour protéger une lecture ou une écriture avec un if (). Pour un dossier, utilisez dir.exists("path/to/folder"). Les versions fs sont fs::file_exists() et fs::dir_exists().

Non — évitez-le. setwd() code en dur l’organisation des dossiers de votre machine dans le script, si bien qu’il ne fonctionne que sur votre ordinateur, dans ce dossier, à cet instant ; il casse pour les collaborateurs, après un déplacement, et en CI. Gardez plutôt votre travail dans un projet (un .Rproj RStudio ou un dépôt Git) et construisez les chemins avec here::here(), qui les résout relativement à la racine du projet sur n’importe quelle machine.

Ils font le même travail, mais fs est plus cohérent. Les fonctions de base R se sont accumulées au fil du temps et sont nommées de façon inégale (file.exists() contre dir.create() ; unlink() pour « supprimer »), renvoient des types de valeur différents, et peuvent se comporter différemment selon le système d’exploitation. Le package fs offre une interface unique, uniforme, vectorisée et multi-plateforme — des noms prévisibles dir_* / file_* / path_*, des séparateurs / partout, et des vecteurs de chemins tidy. Base R n’a besoin d’aucune dépendance ; fs a besoin du package mais est plus agréable pour le code interactif et les pipelines.

Testez votre compréhension

Vous voulez un extrait qui vide et recrée un dossier output/ à la racine de votre projet, prêt pour une nouvelle exécution — en le supprimant s’il est là, puis en le recréant. Écrivez-le pour qu’il tourne à l’identique sur la machine d’un collaborateur (pas de setwd(), pas de chemin absolu).

AstuceIndice

Il vous faut trois pièces : un chemin relatif au projet (pas un chemin absolu), une suppression récursive qui fonctionne sur un répertoire, et un appel de création. Quelle fonction refuse de supprimer un dossier tant que vous ne lui demandez pas de récurser ?

AstuceSolution
library(here)
out <- here("output")                 # project-relative — portable
unlink(out, recursive = TRUE)         # remove the folder if it exists
dir.create(out)                        # recreate it, empty
  • here("output") ancre le chemin à la racine du projet, si bien que le code est identique sur chaque machine.
  • unlink(..., recursive = TRUE) est requis pour supprimer un répertoire — un simple unlink() (ou file.remove()) ne supprimera pas un dossier. unlink() sur un chemin qui n’existe pas est sans danger, c’est donc sûr même au premier passage.
  • dir.create(out) recrée le dossier désormais vide.

La version fs en une ligne est fs::dir_create(here("output")) après fs::dir_delete() — ou recréez simplement, puisque dir_create() s’accommode d’un dossier déjà existant.

Vérification rapide. Vous appelez unlink(file.path(scratch, "results")) (sans recursive = TRUE) sur un dossier. Que se passe-t-il ?

Rien — le dossier reste en place. unlink() refuse de supprimer les répertoires tant que vous ne passez pas recursive = TRUE ; sans cela, « pas même les vides » ne sont supprimés. Ajoutez recursive = TRUE (ou utilisez fs::dir_delete()) pour réellement supprimer une arborescence de répertoires.

Conclusion

Travailler avec des fichiers en R, c’est deux choses : les fonctions et l’habitude. Les fonctions sont petites et faciles à apprendre — list.files(), file.exists(), dir.create(recursive = TRUE), file.copy(), file.rename(), unlink(recursive = TRUE) — le package fs moderne en offrant une version cohérente et multi-plateforme de chacune. L’habitude, c’est ce qui rend votre code reproductible : jamais de setwd(), jamais de chemin absolu codé en dur — ancrez tout avec here::here() et assemblez-le avec file.path() (ou fs::path()). Faites cela, et un script que vous écrivez aujourd’hui tournera encore sur le portable d’un collaborateur, en CI, et sur votre propre machine dans un an.

Voir aussi

Cette page vous a-t-elle été utile ?

Recevez les nouvelles leçons R & Python par e-mail

Pratique, reproductible, sans spam. Désinscription à tout moment.

Double opt-in. Nous ne partageons jamais votre e-mail.

Partager cette pageXLinkedInRedditHN

Citation

BibTeX
@online{kassambara2026,
  author = {Kassambara, Alboukadel},
  title = {Fichiers et répertoires en R : guide reproductible},
  date = {2026-07-18},
  url = {https://www.datanovia.com/blog/files-and-directories-in-r},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
Kassambara, Alboukadel. 2026. “Fichiers et répertoires en R : guide reproductible.” July 18. https://www.datanovia.com/blog/files-and-directories-in-r.