J'essaie de comprendre la façon dont Java résout le chemin relatif lors de la création d'un objet File
.
OS utilisé: Windows
Pour l'extrait ci-dessous, je reçois une IOException
car il ne trouve pas le chemin:
@Test
public void testPathConversion() {
File f = new File("test/test.txt");
try {
f.createNewFile();
System.out.println(f.getPath());
System.out.println(f.getAbsolutePath());
System.out.println(f.getCanonicalPath());
} catch (Exception e) {
e.printStackTrace();
}
}
D'après ce que j'ai compris, Java traite le chemin fourni comme absolu et renvoie une erreur lorsqu'il n'existe pas. C'est donc logique.
Quand je mets à jour le code ci-dessus pour utiliser le chemin relatif:
@Test
public void testPathConversion() {
File f = new File("test/../test.txt");
try {
f.createNewFile();
System.out.println(f.getPath());
System.out.println(f.getAbsolutePath());
System.out.println(f.getCanonicalPath());
} catch (Exception e) {
e.printStackTrace();
}
}
Il crée un nouveau fichier et fournit la sortie ci-dessous:
test\..\test.txt
C:\JavaForTesters\test\..\test.txt
C:\JavaForTesters\test.txt
Dans ce cas, mon hypothèse est que, même si le chemin fourni n'existe pas, car il contient "/../", Java le traite comme un chemin relatif et crée le fichier dans le user.dir
. Cela a donc également un sens.
Mais si je mets à jour le chemin relatif comme ci-dessous:
@Test
public void testPathConversion() {
File f = new File("test/../../test.txt");
try {
f.createNewFile();
System.out.println(f.getPath());
System.out.println(f.getAbsolutePath());
System.out.println(f.getCanonicalPath());
} catch (Exception e) {
e.printStackTrace();
}
}
Ensuite, j'obtiens IOException: l'accès est refusé.
Mes questions sont:
"test/../test.txt"
est traité comme un chemin relatif et crée le fichier dans "user.dir"
mais"test/../../test.txt"
renvoie une erreur? Où tente-t-il de créer le fichier pour le chemin "test/../../test.txt"
?Lorsque le chemin relatif spécifié n'est pas trouvé, le fichier semble avoir été créé dans le user.dir
. Donc, il me semble que les deux scénarios ci-dessous font la même chose:
//scenario 1
File f = new File("test/../test.txt");
f.createNewFile();
//scenario 2
File f = new File("test.txt");
f.createNewFile();
Existe-t-il donc un cas concret dans lequel on utiliserait le scénario 1 au lieu du scénario 2?
Je suppose que je manque quelque chose d’évident ici ou que j’ai des chemins relatifs fondamentalement mal compris. J'ai parcouru les documents Java pour File et je ne suis pas en mesure de trouver une explication à cela. Stack Overflow contient pas mal de questions sur les chemins relatifs, mais celles que j'ai consultées portaient sur des scénarios spécifiques et non sur la façon dont les chemins relatifs sont résolus.
Ce serait formidable si quelqu'un pouvait m'expliquer comment cela fonctionne ou indiquer des liens connexes?
Il existe un concept de working directory
.
Ce répertoire est représenté par un .
(point).
Dans les chemins relatifs, tout le reste lui est relatif.
Il suffit de placer le .
(le répertoire de travail) où vous exécutez votre programme.
Dans certains cas, le répertoire de travail peut être modifié, mais en général
ce que le point représente. Je pense que c'est C:\JavaForTesters\
dans votre cas.
Donc test\..\test.txt
signifie: le sous-répertoire test
dans mon répertoire de travail, puis un niveau supérieur, puis le
fichier test.txt
. C’est fondamentalement la même chose que test.txt
.
Pour plus de détails, consultez ici.
http://docs.Oracle.com/javase/7/docs/api/Java/io/File.html
http://docs.Oracle.com/javase/tutorial/essential/io/pathOps.html
Lorsque votre chemin commence par un répertoire racine, à savoir C:\
dans Windows ou /
dans Unix ou dans le chemin des ressources Java, il est considéré comme un chemin absolu. Tout le reste est relatif, donc
new File("test.txt") is the same as new File("./test.txt")
new File("test/../test.txt") is the same as new File("./test/../test.txt")
La principale différence entre getAbsolutePath
et getCanonicalPath
est que le premier concatène un chemin parent et un chemin enfant, de sorte qu'il peut contenir des points: ..
ou .
. getCanonicalPath
retournera toujours le même chemin pour un fichier particulier.
Remarque: File.equals
utilise une forme abstraite de chemin (getAbsolutePath
) pour comparer des fichiers. Cela signifie donc que deux objets File
de même peuvent ne pas être égaux et que File
s est dangereux à utiliser dans des collections comme Map
ou Set
.
Le répertoire de travail est un concept commun à presque tous les systèmes d'exploitation et à toutes les langues de programme, etc. C'est le répertoire dans lequel votre programme est exécuté. C’est généralement (mais pas toujours, il existe des moyens de le changer) le répertoire dans lequel se trouve l’application.
Les chemins relatifs sont ceux qui commencent sans spécificateur de lecteur. Donc, sous Linux, ils ne commencent pas par un /
, dans Windows, ils ne commencent pas par un C:\
, etc. Ils partent toujours de votre répertoire de travail.
Les chemins absolus sont ceux qui commencent par un spécificateur de lecteur (ou de machine pour les chemins réseau). Ils vont toujours depuis le début de ce lecteur.
Sous Windows et Netbeans, vous pouvez définir le chemin relatif comme suit:
new FileReader("src\\PACKAGE_NAME\\FILENAME");
Sous Linux et Netbeans, vous pouvez définir le chemin relatif comme suit:
new FileReader("src/PACKAGE_NAME/FILENAME");
Si vous avez votre code dans Source Packages
.__, je ne sais pas s'il en va de même pour Eclipse ou autre
Les chemins relatifs peuvent être mieux compris si vous savez comment Java exécute le programme.
Il existe un concept de répertoire de travail lors de l'exécution de programmes en Java. En supposant que vous avez une classe, disons, FileHelper
qui fait le IO sous /User/home/Desktop/projectRoot/src/topLevelPackage/
.
Selon le cas où vous appelez Java
pour exécuter le programme, vous aurez un répertoire de travail différent. Si vous exécutez votre programme de l'intérieur et de l'EDI, il s'agira probablement de projectRoot
.
Dans ce cas, $ projectRoot/src : Java topLevelPackage.FileHelper
sera src
.
Dans ce cas, $ projectRoot : Java -cp src topLevelPackage.FileHelper
sera projectRoot
.
Dans ce cas, $ /User/home/Desktop : Java -cp ./projectRoot/src topLevelPackage.FileHelper
sera Desktop
.
(Assuming $ is your command Prompt with standard Unix-like FileSystem. Similar correspondence/parallels with Windows system)
Ainsi, votre chemin relatif racine (.)
est résolu dans votre répertoire de travail. Ainsi, pour être plus sûr de l'endroit où écrire les fichiers, on considère qu'il faut considérer l'approche ci-dessous.
package topLevelPackage
import Java.io.File;
import Java.nio.file.Path;
import Java.nio.file.Paths;
public class FileHelper {
// Not full implementation, just barebone stub for path
public void createLocalFile() {
// Explicitly get hold of working directory
String workingDir = System.getProperty("user.dir");
Path filePath = Paths.get(workingDir+File.separator+"sampleFile.txt");
// In case we need specific path, traverse that path, rather using . or ..
Path pathToProjectRoot = Paths.get(System.getProperty("user.home"), "Desktop", "projectRoot");
System.out.println(filePath);
System.out.println(pathToProjectRoot);
}
}
J'espère que cela t'aides.
Légèrement lié à la question, mais essayez de vous en tenir à celle-ci. Donc, pas intuitif:
import Java.nio.file.*;
class Main {
public static void main(String[] args) {
Path p1 = Paths.get("/personal/./photos/./readme.txt");
Path p2 = Paths.get("/personal/index.html");
Path p3 = p1.relativize(p2);
System.out.println(p3); //prints ../../../../index.html !!
}
}