Как проверить, является ли данный путь возможным потомком другого пути? - PullRequest
40 голосов
/ 20 января 2011

Я пытаюсь найти, является ли данный путь возможным потомком другого пути с использованием Java. Оба пути могут не существовать.

Скажите, c:\Program Files\My Company\test\My App - это возможный ребенок c:\Program Files.

В настоящее время я делаю это с

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

Ответы [ 9 ]

56 голосов
/ 02 марта 2013

Вы также можете использовать java.nio.file.Path , чтобы сделать это намного проще.Кажется, метод java.nio.file.Path.startsWith обрабатывает все возможные случаи.

Пример:

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

output

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

Если вам нужна большая надежность, вы можете использовать «toRealPath» вместо «toAbsolutePath».

12 голосов
/ 20 января 2011
File parent = maybeChild.getParentFile();
while ( parent != null ) {
  if ( parent.equals( possibleParent ) )
    return true;
  parent = parent.getParentFile();
}
return false;
11 голосов
/ 20 января 2011

Помимо того, что пути могут не существовать (и канонизация может не увенчаться успехом), это выглядит как разумный подход, который должен работать в прямом случае.

Возможно, вы захотите взглянуть на вызов getParentFile () для «возможно дочернего» в цикле, проверяя, соответствует ли он родительскому элементу на каждом шаге.Вы также можете замкнуть сравнение, если родитель не является (настоящим) каталогом.

Возможно, что-то вроде следующего:

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

Обратите внимание, что если вы хотите, чтобы дочерние отношениябыть строгим (т. е. каталог не является дочерним для себя), вы можете изменить начальное присвоение child в строке 9 на child.getParentFile(), поэтому первая проверка происходит в содержащем дочерний каталог.

7 голосов
/ 20 января 2011

Это будет работать для вашего примера. Он также вернет true, если дочерний объект является относительным путем (что часто желательно.)

boolean myCheck(File maybeChild, File possibleParent)
{
    URI parentURI = possibleParent.toURI();
    URI childURI = maybeChild.toURI();
    return !parentURI.relativize(childURI).isAbsolute();
}
4 голосов
/ 20 января 2011

Это, вероятно, будет работать нормально, хотя я бы использовал getCanonicalPath() вместо getAbsolutePath().Это должно нормализовать любые странные пути, такие как x/../y/z, которые в противном случае испортили бы соответствие.

3 голосов
/ 12 августа 2014
maybeChild.getCanonicalPath().startsWith( possibleParent.getCanonicalPath() );
1 голос
/ 15 июля 2015

Будьте в курсе относительных путей!Я думаю, что самое простое решение выглядит примерно так:

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

В Scala вы можете использовать аналогичный подход:

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 голосов
/ 08 мая 2019

Удивительно, но не существует простого, но функционального решения.

Принятый ответ считает те же каталоги дочерними, что неверно.

Вот один из них, использующий java.nio.file.Path Только API:

static boolean isChildPath(Path parent, Path child){
      Path pn = parent.normalize();
      Path cn = child.normalize();
      return cn.getNameCount() > pn.getNameCount() && cn.startsWith(pn);
}

Контрольные примеры:

 @Test
public void testChildPath() {
      assertThat(isChildPath(Paths.get("/FolderA/FolderB/F"), Paths.get("/FolderA/FolderB/F"))).isFalse();
      assertThat(isChildPath(Paths.get("/FolderA/FolderB/F"), Paths.get("/FolderA/FolderB/F/A"))).isTrue();
      assertThat(isChildPath(Paths.get("/FolderA/FolderB/F"), Paths.get("/FolderA/FolderB/F/A.txt"))).isTrue();

      assertThat(isChildPath(Paths.get("/FolderA/FolderB/F"), Paths.get("/FolderA/FolderB/F/../A"))).isFalse();
      assertThat(isChildPath(Paths.get("/FolderA/FolderB/F"), Paths.get("/FolderA/FolderB/FA"))).isFalse();

      assertThat(isChildPath(Paths.get("FolderA"), Paths.get("FolderA"))).isFalse();
      assertThat(isChildPath(Paths.get("FolderA"), Paths.get("FolderA/B"))).isTrue();
      assertThat(isChildPath(Paths.get("FolderA"), Paths.get("FolderA/B"))).isTrue();
      assertThat(isChildPath(Paths.get("FolderA"), Paths.get("FolderAB"))).isFalse();
      assertThat(isChildPath(Paths.get("/FolderA/FolderB/F"), Paths.get("/FolderA/FolderB/F/Z/X/../A"))).isTrue();
}
0 голосов
/ 05 июля 2017

Старый вопрос, но решение до 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;
}

Для полноты решения 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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...