2011-01-20 17 views
32

Próbuję znaleźć, jeśli podana ścieżka jest możliwe dziecko innej ścieżki za pomocą Java. Obie ścieżki mogą nie istnieć.Jak sprawdzić, czy dana ścieżka jest możliwa dziecko innej ścieżki?

Załóżmy, że c:\Program Files\My Company\test\My App jest możliwym dzieckiem z c:\Program Files.

Obecnie robie to z

boolean myCheck(File maybeChild, File possibleParent) 
{ 
    return maybeChild.getAbsolutePath().startsWith(possibleParent.getAbsolutePath()); 
} 
+0

Czy ten przykład wymagać systemu plików IO w ogóle? – user2586917

+0

Prawdopodobny duplikat [Java: Sprawdź, czy ścieżka jest nadrzędną dla pliku] (http://stackoverflow.com/questions/28698125/java-check-if-path-is-parent-of-a-file) – Suma

+0

@Suma : Pytanie, które łączysz, to _duplikat_ tego. – Jayan

Odpowiedz

41

Można również użyć java.nio.file.Path to zrobić o wiele łatwiej. Metoda java.file.Path.startsWith z wydaje się obsługiwać wszystkie możliwe przypadki.

Przykład:

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, ""); 
} 

wyjścia

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

Jeśli trzeba więcej niezawodności Można użyć "toRealPath" zamiast "toAbsolutePath".

+1

Świetne rozwiązanie. Możliwe tylko w Java 7 lub nowszym. –

+1

W jaki sposób obsługuje się w nich ścieżki z '..'? – Max

+0

Metoda "toAbsolutePath" rozwiązuje ".." wewnątrz ścieżki, więc powinna działać. Lepiej to jednak przetestuj. –

4

To prawdopodobnie będzie działać dobrze, jak to jest, chociaż chciałbym użyć getCanonicalPath() zamiast getAbsolutePath(). To powinno znormalizować wszelkie dziwne ścieżki, takie jak x/../y/z, które w przeciwnym razie zepsułyby dopasowanie.

+1

Wielkie dzięki za szybkie i poprawione rozwiązanie! – Jayan

+9

Nie, nie, to ** nie ** jest poprawne! Metoda 'myCheck()' pytającego, nawet gdy jest kanoniczna, fałszywie powie, że 'C: \ Prog' jest dzieckiem' C: \ Program Files'. Zobacz odpowiedź poniżej autorstwa @biziclop. –

7

To będzie działać na twój przykład. Będzie to również powrót true jeśli dziecko jest względna ścieżka (co jest często pożądane.)

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

[Spec] (http://docs.oracle.com/javase/1.4.2/docs/api/java/net/URI.html#relativize (java.net.URI)) mówi, _ "Jeśli [dany URI nie jest dzieckiem], wtedy zwracany jest dany URI. "_ Oznacza to, że prawdopodobnie lepiej jest zmienić kontrolę na" parentURI.relateize (childURI)! = childURI ". W przeciwnym razie twoja funkcja daje fałszywy alarm, jeśli 'maybeChild' jest ścieżką absolutną. – SnakE

+0

masz rację. Prawdopodobnie chciałem powiedzieć, że jeśli 'maybeChild' był _relative_, ale nie był dzieckiem' possibleParent', to twoja metoda nadal zwracałaby 'true'. Ale to nie jest problem, ponieważ 'File.toURI()' gwarantuje zwrócenie bezwzględnego URI, więc 'childURI' jest zawsze bezwzględne. Mimo to czek, który zaproponowałem, powinien również działać dobrze. – SnakE

+0

Jeśli "maybeChild" jest względne, to może potencjalnie być dzieckiem wszystkiego - nie możesz powiedzieć. – finnw

10

Wydzielone z faktu nie mogą istnieć ścieżki (i canonicalisation może nie uda), to wygląda na to rozsądny podejście, które powinno działać w prostym przypadku.

Być może zechcesz sprawdzić wywołanie getParentFile() na "może dziecko" w pętli, sprawdzając, czy pasuje do rodzica w każdym kroku. Możesz także zwierać porównanie, jeśli rodzic nie jest katalogiem (rzeczywistym).

Może coś następującego:

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; 
} 

pamiętać, że jeśli chcesz relacja dziecka do ścisłe (czyli katalog nie jest dzieckiem siebie) można zmienić początkowe child przypisanie on line 9 to child.getParentFile(), więc pierwsze sprawdzenie odbywa się w katalogu zawierającym dziecko.

+2

+1 Chociaż OP nie określił tego, ale w rzeczywistości jest prawdopodobne, że pytanie dotyczy rzeczywistych, istniejących plików, a nie ścieżek. – biziclop

10
File parent = maybeChild.getParentFile(); 
while (parent != null) { 
    if (parent.equals(possibleParent)) 
    return true; 
    parent = parent.getParentFile(); 
} 
return false; 
2

maybeChild.getCanonicalPath(). StartsWith (possibleParent.getCanonicalPath());

1

Bądź świadomy względnych ścieżek! Myślę, że najprostszym rozwiązaniem jest coś takiego:

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) 
    } 
} 

w Scala można mieć podobne podejście:

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) 
0

Stare pytanie, ale pre-1.7 Rozwiązanie:

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; 
} 

Dla kompletności Java 1.7+ rozwiązanie:

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