Préliminaires : lien symbolique (raccourci) ; chemin absolu / relatif.
Java I / O classique
API présente dès le début de java, packagejava.io
.
La classe principale est
java.io.File
, qui représente à la fois un fichier et ou un répertoire.
Exemple d'utilisation :
on suppose que le répertoire home contient un fichier
app.conf
Créer un répertoire
.configdir
et déplacer app.conf
dedans.
=> faire en java ce qui se ferait en bash de cette manière :
cd ~ mkdir .configdir mv app.conf .configdirEn java :
File homedir = new File(System.getProperty("user.home")); File f = new File(homedir, "app.conf"); if (f.exists() && f.isFile() && f.canRead()) { File configdir = new File(f, ".configdir"); configdir.mkdir(); f.renameTo(new File(configdir, ".config")); }
java.io.File
contient un grand nombre de méthodes :
File f = new File("/path/to/file"); // Gestion des droits boolean canR = f.canRead(); boolean canW = f.canWrite(); boolean canX = f.canExecute(); boolean ok; ok = f.setReadOnly(); ok = f.setReadable(true); ok = f.setWritable(false); ok = f.setExecutable(true); // Différents aspects du nom du fichier File absF = f.getAbsoluteFile(); File canF = f.getCanonicalFile(); String absName = f.getAbsolutePath(); String canName = f.getCanonicalPath(); String name = f.getName(); String pName = f.getParent(); URI fileURI = f.toURI(); // Create URI for File path // Metadata boolean exists = f.exists(); boolean isAbs = f.isAbsolute(); boolean isDir = f.isDirectory(); boolean isFile = f.isFile(); boolean isHidden = f.isHidden(); long modTime = f.lastModified(); // milliseconds since epoch boolean updateOK = f.setLastModified(updateTime); // milliseconds long fileLen = f.length(); // Opérations sur le fichier boolean renamed = f.renameTo(new File("/new/filename")); boolean deleted = f.delete(); boolean createdOK = f.createNewFile(); // n'écrase pas le fichier existant // Gestion de fichier temporaire File tmp = File.createTempFile("my-tmp", ".tmp"); tmp.deleteOnExit(); // Gestion de répertoire File dir = new File("/path/to/dir"); boolean createdDir = dir.mkdir(); String[] fileNames = dir.list(); File[] files = dir.listFiles();(voir FileMethodsExamples.java)
Noter qu'il n'y a pas de méthode pour lire le contenu d'un fichier.
Lecture écriture
L'API java pour lire ou écrire dans des fichiers est déroutante au premier abord mais pratique à l'utilisation.Elle comporte 4 classes de base, permattant de travailler soit sur des octets, soit sur des caractères :
Lecture | Ecriture | |
---|---|---|
Caractère (texte) |
Reader | Writer |
Octet (binaire) |
InputStream | OutputStream |
On choisit la source de données et les fonctionnalités utilisables en emboîtant ces classes, qui implémentnent le pattern Decorator.
Streams
Les I/O streams (attention : différents des streams introduits en java8 pour les collections) permettent de gérer des flux d'octets en provenance du disque ou d'autres sources.API organisée autour de 2 classes abstraites :
InputStream
et OutputStream
et de leurs sous-classes.
System.in
et System.out
sont des instances de ces classes.
Exemple : compte le nombre de "a" dans un fichier (code ASCII 97)
public static void main(String[] args){ try (InputStream is = new FileInputStream("test.txt")) { byte[] buf = new byte[4096]; int len, count = 0; while ((len = is.read(buf)) > 0) { for (int i=0; i<len; i++) if (buf[i] == 97) count++; } System.out.println("Trouvé "+ count + " 'a'"); } catch (IOException e) { e.printStackTrace(); } }(voir InputStreamExample.java)
Noter la syntaxe inhabituelle du try, voir try-with-resources plus loin.
Readers et Writers
Travailler avec des octets n'étant pas toujours pratique, deux autres classes abstraites,Reader
et Writer
permettent de travailler au niveau des caractères.
Sous-classes fréquemment utilisées :
FileReader BufferedReader InputStreamReader FileWriter PrintWriter BufferedWriter
try (BufferedReader in = new BufferedReader(new FileReader("test.txt"))) { String line; while((line = in.readLine()) != null) { System.out.println(line); } } catch (IOException e) { }(code dans BufferedReaderExample.java)
Pour comprendre l'intérêt de
BufferedReader
, voir BufferedReaderCompare.java (BufferedReader
est bien plus rapide que FileReader
).
On peut passer d'une représentation "octet" à une représentation "caractère" en utilisant les classes
InputStreamReader
et OutputStreamWriter
.
Fichiers distants
En utilisant des classes du packagejava.net
, on peut lire des fichiers distants :
import java.io.*; import java.net.*; class RemoteExample{ public static void main(String[] args){ URL url = null; try{ url = new URL("https://larzac.info/cnam/index.html"); } catch(MalformedURLException e){ e.printStackTrace(); } try( BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream())); ){ String line; while((line = reader.readLine()) != null){ System.out.println(line); } } catch(IOException e){ e.printStackTrace(); } } }(code dans RemoteExample.java)
Exercice Lecture / Ecriture
try with resources (TWR)
A lire avant : ExceptionsMécanisme introduit en java 7, particulièrement utile pour java.io :
Lorsqu'on fait un
try
"normal", si on utilise des resources qui doivent être libérées, les clauses catch
et finally
sont souvent fastidieuses à écrire.
Cette nouvelle syntaxe permet de passer en paramètre au bloc try des objets qui ont éventuellement besoin de nettoyage.
javac s'occupe alors d'insérer le code nécessaire pour libérer les resources, qu'on n'a donc plus besoin d'écrire.
A UTILISER CHAQUE FOIS QUE C'EST POSSIBLE, c'est à dire pour construire des objets de classes implémentant
java.lang.AutoCloseable
, qui déclare un seule méthode, close()
.
TWR permet de passer en paramètre de
try
la création de plusieurs objets :
try ( BufferedReader in = new BufferedReader(new FileReader("profile")); PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("profile.bak"))) ) { String line; while((line = in.readLine()) != null) { out.println(line); } } catch (IOException e) { // FileNotFoundException, etc. }Similaire à
using
en C#.
Java I / O moderne (java.nio)
Nouvelle API introduite en java 4, fournit un remplacement presque complet dejava.io
; plus simple d'utilisation.
Les sous-classes de
Reader
et Writer
sont toujours utilisées.
Les classes principales sont dans le package
java.nio.file
.
Paths
Représente le chemin d'un fichier dans un système de fichier.Peut être absolu ou relatif
Peut correspondre ou pas à un fichier existant.
Peut représenter un fichier ou un répertoire.
Un path est une séquence de noms de répertoires, éventuellement suivie par un nom de fichier.
Si le premier élément est un composant racine, comme
/
ou C:\
(dépend de l'OS), alors représente un chemin absolu.
L'interface
java.nio.file.Path
représente un path.
java.nio.file.Paths
La classejava.nio.file.Paths
fournit 2 méthodes statiques pour créer un path.
public static Path get(String first, String... more) public static Path get(URI uri)La première méthode (avec
String
) fonctionne de 2 manières, suivant qu'on utilise un ou plusieurs arguments :
Les 2 instructions suivantes sont équivalentes :
Path p1 = Paths.get("/tmp/foo"); Path p2 = Paths.get("/", "tmp", "foo");La seconde méthode attend une uri.
Path p3 = Paths.get(URI.create("file:///Users/joe/FileTest.java"));(code dans CreatePathExamples.java)
import java.nio.file.Path; import java.nio.file.Paths; class PathExamples{ public static void main(String[] args){ // sous Windows : Path home = Paths.get("C:\\Users\\Moi"); Path home = Paths.get("/home/moi"); System.out.println(home); // /home/moi Path p1 = home.resolve("dev/myapp/src"); // Equivalent à : Path p1 = home.resolve(Paths.get("dev/myapp/src")); System.out.println(p1); // /home/moi/dev/myapp/src Path p2 = p1.resolveSibling("tmp.txt"); System.out.println(p2); // /home/moi/dev/myapp/tmp.txt Path p3 = p1.relativize(Paths.get("/home/moi/dev/myapp2/src")); System.out.println(p3); // ../../myapp2/src // part de l'endroit où a été démarrée la JVM Path p4 = Paths.get("config").toAbsolutePath(); System.out.println(p4); // /home/thierry/dev/jobs/cnam/git-repos/public/exemples/java/nio/config Path test; test = p2.getParent(); System.out.println(test); // /home/moi/dev/myapp test = p2.getFileName(); // le dernier élément System.out.println(test); // tmp.txt test = p2.getRoot(); // "/" ou "C:\" ou null pour un chemin relatif System.out.println(test); // / test = p2.getName(0); // le premier element System.out.println(test); // home test = p2.subpath(1, p2.getNameCount()); // tout sauf le premier élément System.out.println(test); // moi/dev/myapp/tmp.txt // Utilise le fait que Path extends Iterable<Path> for (Path component : p4) { System.out.print(component + " - "); } System.out.println(); // home - thierry - dev - jobs - cnam - git-repos - public - exemples - java - nio - config - } }(code dans PathExamples.java)
Remarquer qu'aucune
IOException
n'est lancée.
Files
On peut toujours travailler avec desjava.io.File
ou directement avec des java.nio.file.Path
.
Pour passer de l'un à l'autre, on peut utiliser
Path.toFile()
ou File.toPath()
(since java 1.7).
La classe principale est
java.nio.file.Files
, qui ne contient que des méthodes statiques.
import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.Files; import java.nio.file.SimpleFileVisitor; import java.nio.file.FileVisitResult; import java.nio.file.attribute.BasicFileAttributes; import java.io.IOException; import java.io.PrintWriter; class FilesExamples{ public static void main(String[] args){ try{ String input; // Créer un nouveau fichier // touch toto1.txt Path p1 = Paths.get("toto1.txt").toAbsolutePath(); Files.createFile(p1); // que si n'existe pas (sinon FileAlreadyExistsException) // Ajouter du contenu dans un fichier // cat "du contenu" > toto1.txt String content = "du contenu"; PrintWriter out = new PrintWriter(p1.toFile()); // TWR préférable ici (pas besoin de close()) out.println(content); out.close(); // Autre manière : Files.writeString(p1, content); // Lire le contenu d'un fichier String myContent = Files.readString(p1); // Renommer un fichier // cp toto1.txt toto2.txt Path p2 = p1.resolveSibling("toto2.txt"); Files.copy(p1, p2); System.out.print("toto1.txt et toto2.txt ont été créés - appuyez sur 'Enter' pour les effacer"); input = System.console().readLine(); // Effacer un fichier // rm toto1.txt toto2.txt Files.delete(p1); Files.delete(p2); // Créer des répertoires // mkdir -p test/{dir1,dir2,dir3/dir4} Files.createDirectories(p1.resolveSibling("test/dir1")); Files.createDirectories(p1.resolveSibling("test/dir2")); Files.createDirectories(p1.resolveSibling("test/dir3/dir4")); Path test = p1.resolveSibling("test"); // Parcourir une hiérarchie de fichiers // for i in $(tree -fi test | grep test); do touch $i/test.txt; done; Files.walkFileTree(test, new SimpleFileVisitor(code dans dans FilesExamples.java).() { @Override public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException { if (e == null) { Files.createFile(dir.resolve("test.txt")); return FileVisitResult.CONTINUE; } else { throw e; // directory iteration failed } } }); System.out.print("test/ a été créé - appuyez sur 'Enter' pour l'effacer"); input = System.console().readLine(); // Supprimer récursivement un répertoire // rm -fr test/ Files.walkFileTree(test, new SimpleFileVisitor () { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Files.delete(file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException { if (e == null) { Files.delete(dir); return FileVisitResult.CONTINUE; } else { throw e; // directory iteration failed } } }); } catch(IOException e){ e.printStackTrace(); } } }
Autres méthodes utiles de
Files
:
createDirectories(path) createDirectory(path) createFile(path) exists(path) isDirectory(path) isRegularFile(path)Certaines fonctions utilisent des enums du package :
AccessMode FileVisitOption FileVisitResult LinkOption StandardCopyOption StandardOpenOption