46

I have an absolute path to file A.

I have a relative path to file B from file A's directory. This path may and will use ".." to go up the directory structure in arbitrarily complex ways.

Example A:

  • C:\projects\project1\module7\submodule5\fileA

Example Bs:

  • ..\..\module3\submodule9\subsubmodule32\fileB
  • ..\submodule5\fileB
  • ..\..\module7\..\module4\submodule1\fileB
  • fileB

How do I combine the two in order to get the simplest possible absolute path to file B?

Tom Tresansky
  • 19,364
  • 17
  • 93
  • 129

9 Answers9

53

If I get your problem right, you could do something like this:

File a = new File("/some/abs/path");
File parentFolder = new File(a.getParent());
File b = new File(parentFolder, "../some/relative/path");
String absolute = b.getCanonicalPath(); // may throw IOException
Fabian Steeg
  • 44,988
  • 7
  • 85
  • 112
  • 1
    But the absolute path can have ..'s in it if I do that. I don't want those. Is there a way to get rid of them from the resulting absolute path? – Tom Tresansky Jul 08 '10 at 15:13
  • 9
    @Tom: Using `getCanonicalPath()` instead of `getAbsolutePath()` is probably what you're looking for, in that case. – Tim Stone Jul 08 '10 at 15:19
27
String absolutePath = FileSystems.getDefault().getPath(mayBeRelativePath).normalize().toAbsolutePath().toString();
meyi
  • 7,574
  • 1
  • 15
  • 28
rssdev10
  • 271
  • 3
  • 5
  • Can you explain a bit more what you are doing here? – Stef Geysels Oct 20 '16 at 20:57
  • 3
    `FileSystems.getDefault()` gets default file system. For this file system convert a `mayBeRelativePath` string to a `Path` object. `normalize()` it, that is remove constructions like `../abc/../` from any parts of the path. Transform `toAbsolutePath()` if it is not an absolute yet and get the path as a string. – rssdev10 Dec 12 '16 at 16:48
  • @rssdev10 Thanks a bunch, this worked great in my testing and is only a single line in comparison to all other answers containing 3-20 lines for the same thing. – Leon Jul 06 '17 at 09:18
  • Looks great but Java doesn't see FileSystems as an import. – Andrew Jul 13 '17 at 12:41
  • @Andrew FileSystems are available only from Java 7 & above – Gandhi Feb 08 '18 at 15:16
15

In Java 7 you can also use the Path interface:

Path basePath = FileSystems.getDefault().getPath("C:\\projects\\project1\\module7\\submodule5\\fileA");
Path resolvedPath = basePath.getParent().resolve("..\\..\\module3\\submodule9\\subsubmodule32\\fileB"); // use getParent() if basePath is a file (not a directory) 
Path abolutePath = resolvedPath.normalize();
onlyhuman
  • 381
  • 3
  • 10
5

From your question, if i could get it right, you are looking to get abolute path from relative path, then you can do following.

File b = new File("../some/relative/path");
String absolute = b.getCanonicalPath(); // may throw IOException

or shorthand notation can be,

String absolute = new File("../some/relative/path").getCanonicalPath();
U.Swap
  • 1,921
  • 4
  • 23
  • 41
3

Try FilenameUtils.normalize() from Apache commons-io

Leponzo
  • 624
  • 1
  • 8
  • 20
yegor256
  • 102,010
  • 123
  • 446
  • 597
1

What's better than just creating a utility that converts relative paths to absolute paths is to create a utility that converts any path passed to it into an absolute path so that you don't have to check on the client-side.

The below code works for me in both cases and I've used the String type at the signature of the method (both parameter and return value):

public static String toAbsolutePath(String maybeRelative) {
    Path path = Paths.get(maybeRelative);
    Path effectivePath = path;
    if (!path.isAbsolute()) {
        Path base = Paths.get("");
        effectivePath = base.resolve(path).toAbsolutePath();
    }
    return effectivePath.normalize().toString();
}

Changing the above code to expose Path types on the signature of the method is trivial (and actually easier) but I think that using String on the signature gives more flexibility.

Marcus Junius Brutus
  • 26,087
  • 41
  • 189
  • 331
0

Here is the sample code that works for me.

 public String absolutePath(String relative, String absoluteTo)
    {       
        String[] absoluteDirectories = relative.split("\\\\");
        String[] relativeDirectories = absoluteTo.split("\\\\");
        int relativeLength = relativeDirectories.length;
        int absoluteLength = absoluteDirectories.length; 
        int lastCommonRoot = 0;
        int index;
        for (index = 0; index < relativeLength; index++)
            if (relativeDirectories[index].equals("..\\\\"))
                lastCommonRoot = index;
            else
                break;
        StringBuilder absolutePath = new StringBuilder();
        for (index = 0; index < absoluteLength - lastCommonRoot; index++)
        {  
             if (absoluteDirectories[index].length() > 0) 
                 absolutePath.append(absoluteDirectories[index] + "\\\\");                          
        }
        for (index = lastCommonRoot; index < relativeLength  - lastCommonRoot; 
                                                               index++)
        {  
             if (relativeDirectories[index].length() > 0) 
                 absolutePath.append(relativeDirectories[index] + "\\\\");                          
        }
        return absolutePath.toString();              
    }

Also I the conversion to relative:

public String relativePath(String absolute, String relativeTo) throws Exception
    {       
        String[] absoluteDirectories = absolute.split("\\\\");
        String[] relativeDirectories = relativeTo.split("\\\\");
        int length = absoluteDirectories.length < relativeDirectories.length ?
                        absoluteDirectories.length : relativeDirectories.length;
        int lastCommonRoot = -1;
        int index;
        for (index = 0; index < length; index++)
            if (absoluteDirectories[index].equals(relativeDirectories[index]))
                lastCommonRoot = index;
            else
                break;
        if (lastCommonRoot > -1){
            StringBuilder relativePath = new StringBuilder();
            for (index = lastCommonRoot + 1; index <absoluteDirectories.length;
                                                                         index++)
                if (absoluteDirectories[index].length() > 0)
                    relativePath.append("..\\\\");
            for (index = lastCommonRoot + 1; index <relativeDirectories.length-1;
                                                                         index++)
                relativePath.append(relativeDirectories[index] + "\\\\");
            relativePath.append(relativeDirectories[relativeDirectories.length - 1]);
            return relativePath.toString();         
        }
        else{
            throw new Exception("No common root found between working direcotry and filename");
        }            
    }
code_fish
  • 3,381
  • 5
  • 47
  • 90
iviorel
  • 312
  • 3
  • 10
0

I know it isn't the best solution but can't you just combine the substring of fileA's path from 0 to the lastIndexOf("\") with fileB's path.

Example A:

  • C:\projects\project1\module7\submodule5\fileA

Example Bs:

  • ..\..\module3\submodule9\subsubmodule32\fileB

C:\projects\project1\module7\submodule5\..\..\module3\submodule9\subsubmodule32\fileB

If you don't want the .. in there then, it would take longer, but I recommend going through the path for fileB and keep taking the substring from 0 to the first index of \. Then check the substring. If it is .. then remove the substring from there and remove the substring from fileA's path from lastIndexOf(\) to length. Then repeat. That way you are removing the folders you don't need and the ..s.

So :

Example A:

  • C:\projects\project1\module7\submodule5\fileA

Example Bs:

  • ..\..\module3\submodule9\subsubmodule32\fileB

    --> C:\projects\project1\module3\submodule9\subsubmodule32\fileB

simeg
  • 1,889
  • 2
  • 26
  • 34
Kyra
  • 5,129
  • 5
  • 35
  • 55
-3

Windows path to full Java path.

String winPath = downloadPath+"\\"+dir_contents[i].getName();
String absPath = winPath.replace("\\","\\\\");
simeg
  • 1,889
  • 2
  • 26
  • 34
Ravi
  • 1
  • 1