How to check if a given path is possible child of

2019-01-18 01:12发布

问题:

I am trying to find if given path is possible child of another path using java. Both path may not exist.

Say c:\Program Files\My Company\test\My App is a possible child of c:\Program Files.

Currently I am doing this with

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

回答1:

You can also use java.nio.file.Path to do this much more easily. The java.nio.file.Path.startsWith method seems to handle all possible cases.

Example:

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

outputs

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

If you need more reliability you can use "toRealPath" instead of "toAbsolutePath".



回答2:

Asides from the fact the paths may not exist (and the canonicalisation may not succeed), this looks like a reasonable approach that should work in the straightforward case.

You may want to look at calling getParentFile() on the "maybe child" in a loop, testing if it matches the parent at each step. You can also short-circuit the comparison if the parent isn't a (real) directory.

Perhaps something like the following:

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

Note that if you want the child relationship to be strict (i.e. a directory is not a child of itself) you can change the initial child assignment on line 9 to be child.getParentFile() so the first check happens on the child's containing directory.



回答3:

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


回答4:

This will work for your example. It will also return true if the child is a relative path (which is often desirable.)

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


回答5:

That will probably work fine as it is, although I would use getCanonicalPath() rather than getAbsolutePath(). This should normalize any weird paths like x/../y/z which would otherwise screw up the matching.



回答6:

maybeChild.getCanonicalPath().startsWith( possibleParent.getCanonicalPath() );


回答7:

Be aware of relative paths! I think that simplest solution is something like this:

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

In scala you can have similar approach:

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)


回答8:

Old question but a pre-1.7 solution:

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

For completeness the java 1.7+ solution:

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


标签: java file-io