Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: How to get the parts of a path

This should be fairly simple, but I'm just stuck. Say you have the path /a/b/c/. I'd like to convert that into an array containing:

  • /
  • /a/
  • /a/b/
  • /a/b/c/

The slash at the beginning and the end should be optional. Any one care to help?

I'm going to use it for a function that creates a directory, and I want it to create all the missing parts too and not fail if for example a or b doesn't exist.


Update: I would of course use File.mkdirs() if I could, but this isn't on the local file system. It's to simplify interfacing with an SFTP library which only has a mkdir method taking a path in the form of a string.

like image 604
Svish Avatar asked Jun 08 '11 13:06

Svish


5 Answers

Why not just use File.mkdirs()?


edit: per your requirement not to use File.mkdirs():

I still think it's easier to use File as a helper class:

package com.example.test;

import java.io.File;
import java.util.LinkedList;
import java.util.List;

public class FileSplitter {
    final private File path;

    public List<String> getPathStrings()
    {
        LinkedList<String> list = new LinkedList<String>();
        File p = this.path;
        while (p != null)
        {
            list.addFirst(p.getPath());
            p = p.getParentFile();
        }
        return list;
    }

    public FileSplitter(File path) { this.path = path; }

    public static void main(String[] args) {
        doit(new File("/foo/bar/baz"));
        doit(new File("/bam/biff/boom/pow"));
    }

    private static void doit(File file) {
        for (String s : new FileSplitter(file)
                .getPathStrings())
            System.out.println(s);      
    }
}

On my machine (windows) this prints out:

\
\foo
\foo\bar
\foo\bar\baz
\
\bam
\bam\biff
\bam\biff\boom
\bam\biff\boom\pow

If you have a need to use forward slashes no matter what, then I'd either implement using strings rather than Files, or just do a .replace('\\','/') on point of use.


Finally, here's an approach that might be more helpful for your end application.

It has the same output as the previous, but lends itself to an inversion of control where you can perform your custom mkdir() as a pseudo-Runnable to be passed in as a step to a path iterator:

package com.example.test;

import java.io.File;

public class PathRunner
{
    final private File path;
    public PathRunner(File path) { 
        this.path = path; 
    }

    public interface Step
    {
        public boolean step(File path);
    }

    public boolean run(Step step) 
    {
        return run(step, this.path);
    }
    private boolean run(Step step, File p)
    {
        if (p == null)
            return true;
        else if (!run(step, p.getParentFile()))
            return false;
        else
            return step.step(p);
    }

    /**** test methods ****/

    public static void main(String[] args) {
        doit(new File("/foo/bar/baz"));
        doit(new File("/bam/biff/boom/pow"));
    }
    private static boolean doit(File path) {
        Step step = new Step()
        {
            @Override public boolean step(File path) {
                System.out.println(path);
                return true;
                /* in a mkdir operation, here's where you would call: 

                return yourObject.mkdir(
                    path.getPath().replace('\\', '/')
                );
                 */
            }               
        };
        return new PathRunner(path).run(step);
    }
}
like image 136
Jason S Avatar answered Oct 13 '22 00:10

Jason S


If you need something primitive. Try split and append.

public class StackOverflow {

    public static void main(String args[]) {

        String[] folders = "/a/b/c/".split("/");
        String[] paths = new String[folders.length];
        String path = "";

        for (int i = 0; i < folders.length; i++) {
            path +=   folders[i] + "/";
            paths[i] = path;
        }
    }
}

This the output of code block:

run:
/
/a/
/a/b/
/a/b/c/
BUILD SUCCESSFUL (total time: 0 seconds)
like image 21
Kerem Baydoğan Avatar answered Oct 12 '22 23:10

Kerem Baydoğan


The File class supports this.

public static void main(String... args) {
    split(new File("/a/b/c/d/e"));
    split(new File("\\A\\B\\C\\D\\E"));
}

private static void split(File file) {
    File parent = file.getParentFile();
    if (parent != null) split(parent);
    System.out.println(file);
}

on windows prints

\
\a
\a\b
\a\b\c
\a\b\c\d
\a\b\c\d\e
\
\A
\A\B
\A\B\C
\A\B\C\D
\A\B\C\D\E
like image 31
Peter Lawrey Avatar answered Oct 13 '22 00:10

Peter Lawrey


No need to do this. File.mkdirs() instead

like image 40
I82Much Avatar answered Oct 12 '22 23:10

I82Much


Ended up with this code:

   public String[] componizePath(String path)
   {
      ArrayList<String> parts = new ArrayList<String>();  

      int index = 0;
      while(index < path.length())
      {
         if(path.charAt(index) == '/' || index == path.length()-1)
         {
            parts.add(path.substring(0, index+1));
         }
         index++;
      }

      return parts.toArray(new String[0]);
   }

JUnit tests:

   @Test
   public void componizePath_EmptyPath()
   {
      String[] actual = getSftp().componizePath("");
      String[] expected = new String[0];
      assertArrayEquals(expected, actual);
   }

   @Test
   public void componizePath_RootPath()
   {
      String[] actual = getSftp().componizePath("/");
      String[] expected = new String[] {"/"};
      assertArrayEquals(expected, actual);
   }

   @Test
   public void componizePath_SimplePath()
   {
      String[] actual = getSftp().componizePath("a");
      String[] expected = new String[] {"a"};
      assertArrayEquals(expected, actual);
   }

   @Test
   public void componizePath_SimplePathWithTrailingSlash()
   {
      String[] actual = getSftp().componizePath("a/");
      String[] expected = new String[] {"a/"};
      assertArrayEquals(expected, actual);
   }

   @Test
   public void componizePath_ComplexerPath()
   {
      String[] actual = getSftp().componizePath("a/b/cc");
      String[] expected = new String[] {"a/", "a/b/", "a/b/cc"};
      assertArrayEquals(expected, actual);
   }

   @Test
   public void componizePath_ComplexerPathWithTrailingSlash()
   {
      String[] actual = getSftp().componizePath("a/b/c/");
      String[] expected = new String[] {"a/", "a/b/", "a/b/c/"};
      assertArrayEquals(expected, actual);
   }

   @Test
   public void componizePath_ComplexerPathWithLeadingSlash()
   {
      String[] actual = getSftp().componizePath("/a/b/c");
      String[] expected = new String[] {"/", "/a/", "/a/b/", "/a/b/c"};
      assertArrayEquals(expected, actual);
   }
like image 45
Svish Avatar answered Oct 12 '22 23:10

Svish