public class SpringTest1_3 { private static final String FOLDER_SEPARATOR = "/"; private static final String WINDOWS_FOLDER_SEPARATOR = "\\"; private static final String TOP_PATH = ".."; private static final String CURRENT_PATH = "."; private static final char EXTENSION_SEPARATOR = '.'; public static void main(String[] args) { String a = "f/../../a/b/c/../../spring_test3.xml"; System.out.println(cleanPath(a)); } public static String cleanPath(String path) { if (path == null) { return null; } // 将所有的 \\ 替换成 / ,window 中的路径可能以 \\ 分隔 ,mac 或者 linux 路径分隔一般以 / 来分隔 String pathToUse = replace(path, "\\", "/"); // 如果有 classpath: 或 file: 等,将前缀保留下来 int prefixIndex = pathToUse.indexOf(":"); String prefix = ""; if (prefixIndex != -1) { // 如果字符串中包含 : prefix = pathToUse.substring(0, prefixIndex + 1); if (prefix.contains("/")) { // 如果前缀中包含 / prefix = ""; } else { // 如果前缀中不包含 / ,则截取前缀保留 ,不做计算 pathToUse = pathToUse.substring(prefixIndex + 1); } } if (pathToUse.startsWith("/")) { // 如果去除前缀classpath: 的pathToUse 以 / 开头 ,classpath:/spring_1_100\config_1_10/spring_test1/./a/b/c/../../spring_test3.xml prefix = prefix + "/"; pathToUse = pathToUse.substring(1); } // 以 / 分隔得到字符串数组 String[] pathArray = delimitedListToStringArray(pathToUse, "/"); List pathElements = new LinkedList(); int tops = 0; // 反序遍历 for (int i = pathArray.length - 1; i >= 0; i--) { String element = pathArray[i]; // 如果 pathArray[i] 是 . 则直接略过 if (".".equals(element)) { } else if ("..".equals(element)) { // 如果pathArray[i] 是 .. 则 top ++ tops++; } else { if (tops > 0) { // 抵消掉路径 , 比如 config_1_10/spring_test1/./a/b/c/../../spring_test3.xml 当找到第一个 .. ,top = 1 // 第二个 .. 时,top =2 ,当循环到 c 时,top = 2 ,让 top -- ,top = 1 ,抵消掉 c, // 从后面向前查找,查找到第二个 .. 抵消掉 c ,第一个 .. 则抵消掉 b . tops--; } else { // 如果没有 .. 抵消,直接保存到集合中 pathElements.add(0, element); } } } // 如果 .. 没有被抵消完全,在集合开头加上 .. // f/../../a/b/c/../../spring_test3.xml 解析成 ../a/spring_test3.xml for (int i = 0; i < tops; i++) { pathElements.add(0, ".."); } // 将之前保留的前缀加到开头,同时对集合中的每个元素以 / 分隔 return prefix + collectionToDelimitedString(pathElements, "/"); } public static String collectionToDelimitedString(Collection<?> coll, String delim) { return collectionToDelimitedString(coll, delim, "", ""); } public static String collectionToDelimitedString(Collection<?> coll, String delim, String prefix, String suffix) { if (CollectionUtils.isEmpty(coll)) { return ""; } StringBuilder sb = new StringBuilder(); Iterator<?> it = coll.iterator(); while (it.hasNext()) { sb.append(prefix).append(it.next()).append(suffix); if (it.hasNext()) { sb.append(delim); } } return sb.toString(); } public static String[] delimitedListToStringArray(String str, String delimiter) { return delimitedListToStringArray(str, delimiter, null); } public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) { if (str == null) { return new String[0]; } if (delimiter == null) { return new String[] {str}; } List result = new ArrayList(); if ("".equals(delimiter)) { for (int i = 0; i < str.length(); i++) { result.add(deleteAny(str.substring(i, i + 1), charsToDelete)); } } else { int pos = 0; int delPos; while ((delPos = str.indexOf(delimiter, pos)) != -1) { result.add(deleteAny(str.substring(pos, delPos), charsToDelete)); pos = delPos + delimiter.length(); } if (str.length() > 0 && pos <= str.length()) { result.add(deleteAny(str.substring(pos), charsToDelete)); } } return toStringArray(result); } public static String[] toStringArray(Collection collection) { if (collection == null) { return null; } return collection.toArray(new String[collection.size()]); } public static String deleteAny(String inString, String charsToDelete) { if (!hasLength(inString) || !hasLength(charsToDelete)) { return inString; } StringBuilder sb = new StringBuilder(); for (int i = 0; i < inString.length(); i++) { char c = inString.charAt(i); if (charsToDelete.indexOf(c) == -1) { sb.append(c); } } return sb.toString(); } public static String replace(String inString, String oldPattern, String newPattern) { if (!hasLength(inString) || !hasLength(oldPattern) || newPattern == null) { return inString; } StringBuilder sb = new StringBuilder(); int pos = 0; int index = inString.indexOf(oldPattern); int patLen = oldPattern.length(); while (index >= 0) { sb.append(inString.substring(pos, index)); sb.append(newPattern); pos = index + patLen; index = inString.indexOf(oldPattern, pos); } sb.append(inString.substring(pos)); return sb.toString(); } public static boolean hasLength(CharSequence str) { return (str != null && str.length() > 0); } }
结果输出:
…/a/spring_test3.xml
网上有人总结【因为是在循环体内,按照tokens的数量,逐个发现并试图逐个抵消“. ./”,这可能会存在的一个问题是,tokens不够“抵消”。比如"/a/. ./. ./b.xml",需要抵消两次,但最多允许抵消一次(“a/”),但上述循环体内的处理对此无能为力,最终处理成为只剩下b.xml了,这肯定不是想要的结果。但有一点可以肯定的是,如果是绝对路径并且正确,就一定不会存在这个问题;但如果是相对路径,用此就需要格外小心了!或许想方设法将相对路径转为绝对路径不失为上策。】
本文的 github 地址
https://github.com/quyixiao/spring_tiny/blob/master/src/main/java/com/spring_1_100/test_1_10/test/SpringTest1_3.java