web-dev-qa-db-fra.com

Comment vérifier si un chemin donné est un enfant possible d'un autre chemin?

J'essaie de trouver si le chemin donné est un enfant possible d'un autre chemin en utilisant Java. Les deux chemins peuvent ne pas exister.

Dites que c:\Program Files\My Company\test\My App est un enfant possible de c:\Program Files.

Actuellement je le fais avec

boolean myCheck(File maybeChild, File possibleParent)
{
    return maybeChild.getAbsolutePath().startsWith( possibleParent.getAbsolutePath());
}
34
Jayan

Vous pouvez également utiliser Java.nio.file.Path pour le faire beaucoup plus facilement. La méthode Java.nio.file.Path.startsWith semble traiter tous les cas possibles.

Exemple:

private static void isChild(Path child, String parentText) {
    Path parent = Paths.get(parentText).toAbsolutePath();
    System.out.println(parentText + " = " + child.startsWith(parent));
}

public static void main(String[] args) {
    Path child = Paths.get("/FolderA/FolderB/File").toAbsolutePath();
    isChild(child, "/FolderA/FolderB/File");
    isChild(child, "/FolderA/FolderB/F");
    isChild(child, "/FolderA/FolderB");
    isChild(child, "/FolderA/Folder");
    isChild(child, "/FolderA");
    isChild(child, "/Folder");
    isChild(child, "/");
    isChild(child, "");
}

les sorties

/FolderA/FolderB/File = true
/FolderA/FolderB/F = false
/FolderA/FolderB = true
/FolderA/Folder = false
/FolderA = true
/Folder = false
/ = true
 = false

Si vous avez besoin de plus de fiabilité, vous pouvez utiliser "toRealPath" au lieu de "toAbsolutePath".

48
Jecho Jekov
File parent = maybeChild.getParentFile();
while ( parent != null ) {
  if ( parent.equals( possibleParent ) )
    return true;
  parent = parent.getParentFile();
}
return false;
11
biziclop

Mis à part le fait que les chemins peuvent ne pas exister (et que la canonisation peut ne pas aboutir), cela semble être une approche raisonnable qui devrait fonctionner dans le cas simple.

Vous voudrez peut-être appeler getParentFile () sur "peut-être l'enfant" dans une boucle, en vérifiant si cela correspond au parent à chaque étape. Vous pouvez également court-circuiter la comparaison si le parent n'est pas un répertoire (réel).

Peut-être quelque chose comme ce qui suit:

boolean myCheck(File maybeChild, File possibleParent) throws IOException
{
    final File parent = possibleParent.getCanonicalFile();
    if (!parent.exists() || !parent.isDirectory()) {
        // this cannot possibly be the parent
        return false;
    }

    File child = maybeChild.getCanonicalFile();
    while (child != null) {
        if (child.equals(parent)) {
            return true;
        }
        child = child.getParentFile();
    }
    // No match found, and we've hit the root directory
    return false;
}

Notez que si vous souhaitez que la relation enfant soit strict (c’est-à-dire qu’un répertoire n’est pas un enfant en lui-même), vous pouvez modifier l’affectation initiale child de la ligne 9 par child.getParentFile() pour que le premier contrôle ait lieu annuaire.

11
Andrzej Doyle

Cela fonctionnera pour votre exemple. Il retournera également true si l'enfant est un chemin relatif (Ce qui est souvent souhaitable.)

boolean myCheck(File maybeChild, File possibleParent)
{
    URI parentURI = possibleParent.toURI();
    URI childURI = maybeChild.toURI();
    return !parentURI.relativize(childURI).isAbsolute();
}
7
finnw

Cela fonctionnera probablement tel quel, bien que j'utiliserais getCanonicalPath() plutôt que getAbsolutePath(). Cela devrait normaliser tous les chemins étranges comme x/../y/z qui autrement bousillerait la correspondance.

4
skaffman
maybeChild.getCanonicalPath().startsWith( possibleParent.getCanonicalPath() );
2
user1988293

Soyez conscient des chemins relatifs! Je pense que la solution la plus simple ressemble à ceci:

public boolean myCheck(File maybeChild, File possibleParent) {
  if (requestedFile.isAbsolute) {
    return possibleParent.resolve(maybeChild).normalize().toAbsolutePath.startsWith(possibleParent.normalize().toAbsolutePath)
  } else {
    return maybeChild.normalize().toAbsolutePath.startsWith(possibleParent.normalize().toAbsolutePath)
  }
}

En scala, vous pouvez avoir une approche similaire:

val baseDir = Paths.get("/home/luvar/tmp")
val baseDirF = baseDir.toFile
//val requestedFile = Paths.get("file1")
val requestedFile = Paths.get("../.viminfo")
val fileToBeRead = if (requestedFile.isAbsolute) {
  requestedFile
} else {
  baseDir.resolve(requestedFile)
}
fileToBeRead.toAbsolutePath
baseDir.toAbsolutePath
fileToBeRead.normalize()
baseDir.normalize()
val isSubpath = fileToBeRead.normalize().toAbsolutePath.startsWith(baseDir.normalize().toAbsolutePath)
1
Ľubomír Varga

Ancienne question mais solution antérieure à la 1.7:

public boolean startsWith(String possibleRoot, String possibleChildOrSame) {
        String[] possiblePath = new File(possibleRoot).getAbsolutePath().replace('\\', '/').split("/");
        String[] possibleChildOrSamePath = new File(possibleChildOrSame).getAbsolutePath().replace('\\', '/').split("/");

        if (possibleChildOrSamePath.length < possiblePath.length) {
            return false;
        }

        // not ignoring case
        for (int i = 0; i < possiblePath.length; i++) {
            if (!possiblePath[i].equals(possibleChildOrSamePath[i])) {
                return false;
            }
        }
        return true;
}

Pour être complet, la solution Java 1.7+:

public boolean startsWith(String possibleRoot, String possibleChildOrSame) {
        Path p1 = Paths.get(possibleChildOrSame).toAbsolutePath();
        Path p2 = Paths.get(possibleRoot).toAbsolutePath();
        return p1.startsWith(p2);
}