生成规范路径

时间:2010-04-21 14:15:14

标签: java path

是否有人知道我可以用来生成规范路径的任何Java库(基本上删除了反向引用)。

我需要做以下事情:

原始路径 - >规范路径

/../foo/       -> /foo
/foo/          -> /foo
/../../../     -> /
/./foo/./      -> /foo
//foo//bar     -> /foo/bar
//foo/../bar   -> /bar

等...

目前我懒洋洋地依赖使用:

 new File("/", path).getCanonicalPath();

但这解决了针对实际文件系统的路径,并且已同步。

   java.lang.Thread.State: BLOCKED (on object monitor)
        at java.io.ExpiringCache.get(ExpiringCache.java:55)
        - waiting to lock <0x93a0d180> (a java.io.ExpiringCache)
        at java.io.UnixFileSystem.canonicalize(UnixFileSystem.java:137)
        at java.io.File.getCanonicalPath(File.java:559)

我的规范化路径在我的文件系统中不存在,因此只需该方法的逻辑就可以了,因此不需要任何同步。我希望有一个经过良好测试的库,而不是自己编写。

7 个答案:

答案 0 :(得分:21)

我认为您可以使用URI类来执行此操作;例如如果路径中不包含需要在URI路径组件中转义的字符,则可以执行此操作。

String normalized = new URI(path).normalize().getPath();

如果路径包含(或可能包含)需要转义的字符,则多参数构造函数将转义path参数,并且可以为其他参数提供null

注意:

  1. 上面通过将文件路径视为相对URI来规范化文件路径。如果要标准化整个URI ...包括(可选)方案,权限和其他组件,请不要调用getPath()

  2. URI规范化不涉及像文件规范化那样查看文件系统。但另一方面,当路径中存在符号链接时,规范化与规范化的行为不同。

答案 1 :(得分:17)

使用 Apache Commons IO (一个着名且经过良好测试的库)

public static String normalize(String filename)

将完全满足您的需求。

示例:

String result = FilenameUtils.normalize(myFile.getAbsolutePath());

答案 2 :(得分:11)

如果您不需要路径规范化而只需要规范化,那么在Java 7中您可以使用java.nio.file.Path.normalize方法。 根据{{​​3}}:

  

此方法不访问文件系统;路径可能找不到存在的文件。

如果使用File对象,可以使用以下内容:

file.toPath().normalize().toFile()

答案 3 :(得分:4)

您可以尝试这样的算法:

String collapsePath(String path) {
    /* Split into directory parts */
    String[] directories = path.split("/");
    String[] newDirectories = new String[directories.length];
    int i, j = 0;

    for (i=0; i<directories.length; i++) {
        /* Ignore the previous directory if it is a double dot */
        if (directories[i].equals("..") && j > 0)
            newDirectories[j--] = "";
        /* Completely ignore single dots */
        else if (! directories[i].equals("."))
            newDirectories[j++] = directories[i];
    }

    /* Ah, what I would give for String.join() */
    String newPath = new String();
    for (i=0; i < j; i++)
        newPath = newPath + "/" + newDirectories[i];
    return newPath;
}

不完美;它在目录数量上是线性的,但确实在内存中复制。

答案 4 :(得分:0)

哪种路径被认定为规范路径取决于操作系统。 这就是Java需要在文件系统上检查它的原因。 因此,在不知道操作系统的情况下测试路径没有简单的逻辑。

答案 5 :(得分:0)

因此,尽管规范化可以解决问题,但是此过程公开了比简单调用Paths.normalize()

更多的Java API。

说我想找到一个不在文件系统当前目录中的文件。 我的工作代码文件是

myproject/src/JavaCode.java

位于myproject / src /中。我的文件位于

../../data/myfile.txt

我正在测试从JavaCode.java运行我的代码的程序

public static void main(String[] args) { 
    findFile("../../data","myfile.txt");
    System.out.println("Found it.");
}
public static File findFile(String inputPath, String inputFile) {
    File dataDir = new File("").getAbsoluteFile(); // points dataDir to working directory
    String delimiters = "" + '\\' + '/';           // dealing with different system separators
    StringTokenizer st = new StringTokenizer(inputPath, delimiters);
    while(st.hasMoreTokens()) {
        String s = st.nextToken();
        if(s.trim().isEmpty() || s.equals(".")) 
            continue;
        else if(s.equals("..")) 
            dataDir = dataDir.getParentFile();
        else {
            dataDir = new File(dataDir, s);
            if(!dataDir.exists())
                throw new RuntimeException("Data folder does not exist.");
        }
    }
    return new File(dataDir, inputFile);    
}

已将文件放置在指定位置,应打印“找到它”。

答案 6 :(得分:-1)

我假设你有字符串,你想要字符串,你现在可以使用Java 7,而你的默认文件系统使用'/'作为路径分隔符,所以试试:

String output = FileSystems.getDefault().getPath(input).normalize().toString();

您可以尝试使用:

/**
 * Input           Output
 * /../foo/     -> /foo
 * /foo/        -> /foo
 * /../../../   -> /
 * /./foo/./    -> /foo
 * //foo//bar   -> /foo/bar
 * //foo/../bar -> /bar
 */
@Test
public void testNormalizedPath() throws URISyntaxException, IOException {
    String[] in = new String[]{"/../foo/", "/foo/", "/../../../", "/./foo/./",
            "//foo/bar", "//foo/../bar", "/", "/foo"};
    String[] ex = new String[]{"/foo", "/foo", "/", "/foo", "/foo/bar", "/bar", "/", "/foo"};
    FileSystem fs = FileSystems.getDefault();
    for (int i = 0; i < in.length; i++) {
        assertEquals(ex[i], fs.getPath(in[i]).normalize().toString());
    }
}