目錄對比和搜索工具

目錄對比搜索工具:

我遇到需要對比兩個目錄結構很相似的目錄的結構差異,也可以對比多個目錄,所以寫了這個工具,可能有人也會有這樣的需要,所以發出來。需求是這樣的:如下表示a和b兩個目錄,我需要工具告訴我b目錄的d2目錄下有個不符合*f*.txt的文件0000.txt,並且告訴我b目錄多了個d3的目錄。

├───a
│   │   f0.txt
│   │   f1.txt
│   │
│   └───d1
│       ├───d1
│       │       abc_f1.txt
│       │       def_f2.txt
│       │
│       └───d2
│               22_f1.txt
│               f2.txt
│
└───b
    │   f2.txt
    │   f3.txt
    │
    └───d1
        ├───d1
        │       ccc_f1.txt
        │       sss_f1.txt
        │
        ├───d2
        │       0000.txt
        │       123_f1.txt
        │
        └───d3

我寫這個工具類輸入如下參數就可以告訴我有個不符合規律的文件0000.txt,b比a目錄多了個d3的目錄

參數:

--paths
D:\tmp\data\a,D:\tmp\data\b
--replace
*f*.txt,/a/,/b/,/d*/
--outfile
compare_result.txt

執行結果:

-----------------1-------------------

D:\tmp\data\a
count: 6

-----------------1-------------------




-----------------2-------------------

D:\tmp\data\b
count: 6

Only in current path (count: 1):



D:\tmp\d*\*\d*\d*\0000.txt (count: 1):

D:\tmp\data\b\d1\d2\0000.txt


D:\tmp\d*\*\d*\d3 (count: 1):

D:\tmp\data\b\d1\d3

-----------------2-------------------

 

搜索功能:

比如我需要搜索文件名以f1.txt結尾的文件:

參數:

--paths
D:\tmp\data\a,D:\tmp\data\b
--search
*f1.txt
--outfile
search_result.txt

不寫--outfile就直接打印到屏幕上

執行結果:

Found 6 items in all directory:



D:\tmp\data\a

Found 3 items in current directory:

D:\tmp\data\a\f1.txt

D:\tmp\data\a\d1\d1\abc_f1.txt

D:\tmp\data\a\d1\d2\22_f1.txt



D:\tmp\data\b

Found 3 items in current directory:

D:\tmp\data\b\d1\d1\ccc_f1.txt

D:\tmp\data\b\d1\d1\sss_f1.txt

D:\tmp\data\b\d1\d2\123_f1.txt


最後貼上代碼:

DirectoryCompareAndSearchTool.java

import java.io.File;
import java.io.PrintStream;
import java.nio.file.Paths;
import java.util.*;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class DirectoryCompareAndSearchTool {
    private final static String HELP_TEXT = "Options: --paths <path>[,<path>].. [--replace <str>[,<str>..] [--exclude <patten>[,<patten>..]] [--search <str>[,<str>..] [--outfile <path>]";

    public static void main(String[] args) {
        registerExceptionHandler(Thread.currentThread(), args);

        InputArgs inputArgs = parseArgs(args);

        List<String> result = doWork(inputArgs);

        handleResult(result, inputArgs);
    }

    private static void registerExceptionHandler(Thread thread, String[] args) {
        thread.setUncaughtExceptionHandler((t, e) -> {
            if (e.getClass() != IllegalArgumentException.class) {
                for (StackTraceElement element : e.getStackTrace()) {
                    if (DirectoryCompareAndSearchTool.class.getName().equals(element.getClassName())) {
                        System.err.println("Unknown error: " + e.getClass().getSimpleName() + ":" + e.getMessage() + ", on " + element);
                        break;
                    }
                }
            } else {
                System.err.println("Error: " + e.getMessage());
            }
            System.out.println(HELP_TEXT);
            if (args != null && args.length > 0) {
                System.out.println("\ninput args:");
                Arrays.asList(args).forEach(System.out::println);
            }
        });
    }

    private static void handleResult(List<String> result, InputArgs inputArgs) {
        if (inputArgs.outFile != null) {
            File file = new File(inputArgs.outFile);
            try (PrintStream ps = new PrintStream(file)) {
                for (String line : result) {
                    ps.println(line);
                }
                System.out.println("Result written to file: " + file.getAbsolutePath());
            } catch (Exception e) {
                System.err.println(e.getMessage());
            }
        } else {
            result.forEach(System.out::println);
        }
    }

    private static List<String> doWork(InputArgs inputArgs) {
        List<List<String>> lists = new ArrayList<>();
        for (String path : inputArgs.pathList) {
            lists.add(getFiles(path, file -> {
                if (inputArgs.excludeList == null) {
                    return true;
                }
                for (Pattern exclude : inputArgs.excludeList) {
                    if (exclude.matcher(file.getAbsolutePath()).matches()) {
                        return false;
                    }
                }
                return true;
            }));
        }

        List<List<String[]>> pathInfoLists = new ArrayList<>();
        for (List<String> list : lists) {
            List<String[]> pathInfoList = new ArrayList<>();
            pathInfoLists.add(pathInfoList);
            int size = list.size();
            for (int i = 0; i < size; i++) {
                pathInfoList.add(new String[]{replacePath(list.get(i), inputArgs), list.get(i)});
            }
            Collections.sort(pathInfoList, Comparator.comparing(o -> o[0]));
        }


        List<String> result;
        if (inputArgs.searchMode) {
            result = doSearch(pathInfoLists, inputArgs);
        } else {
            result = doCompare(pathInfoLists, inputArgs);
        }

        return result;
    }

    private static List<String> doCompare(List<List<String[]>> pathInfoLists, InputArgs inputArgs) {
        List<String> result = new ArrayList<>();

        for (int i = 0; i < inputArgs.pathList.size(); i++) {
            result.add("-----------------" + (i + 1) + "-------------------");
            result.add(inputArgs.pathList.get(i));
            result.add("count: " + pathInfoLists.get(i).size());

            List<String[]> list = pathInfoLists.get(i);
            boolean curFirstFind = true;
            int specialCount = 0;
            int firstIndex = result.size();
            String lastPatten = null;
            int lastPattenStart = -1;
            int samePattenCount = 0;
            for (String[] path : list) {
                boolean find = false;
                for (List<String[]> l : pathInfoLists) {
                    if (l == list) {
                        continue;
                    }
                    for (String[] item : l) {
                        if (item[0].equals(path[0])) {
                            find = true;
                            break;
                        }
                    }
                }
                if (!find) {
                    if (curFirstFind) {
                        curFirstFind = false;
                        result.add("In processing..");
                    }
                    if (!path[0].equals(lastPatten)) {
                        if (lastPattenStart != -1) {
                            result.set(lastPattenStart, System.lineSeparator() + lastPatten + " (count: " + samePattenCount + "):");
                        }
                        lastPatten = path[0];
                        if (!path[0].equals(path[1])) {
                            lastPattenStart = result.size();
                            samePattenCount = 1;
                            result.add("In processing..");
                        } else {
                            if (lastPattenStart != -1) {
                                result.add("");
                            }
                            lastPattenStart = -1;
                        }
                    } else {
                        ++samePattenCount;
                    }
                    result.add(path[1]);
                    ++specialCount;
                }
            }
            if (lastPattenStart != -1) {
                result.set(lastPattenStart, "\n" + lastPatten + " (count: " + samePattenCount + "):");
            }
            if (specialCount > 0) {
                result.set(firstIndex, "Only in current path (count: " + specialCount + "):");
            }

            result.add("-----------------" + (i + 1) + "-------------------");
            result.add(System.lineSeparator());
        }
        return result;
    }

    private static List<String> doSearch(List<List<String[]>> pathInfoLists, InputArgs inputArgs) {
        List<String> result = new ArrayList<>();
        result.add("Search ..");
        int count = 0;
        for (int i = 0; i < inputArgs.pathList.size(); i++) {
            List<String[]> list = pathInfoLists.get(i);
            result.add(System.lineSeparator() + inputArgs.pathList.get(i));
            int index = result.size();
            result.add("");
            int currentCount = 0;
            for (String[] path : list) {
                if (!path[0].equals(path[1])) {
                    result.add(path[1]);
                    ++count;
                    ++currentCount;
                }
            }
            result.set(index, "Found " + currentCount + " items in the current directory:");
        }
        result.set(0, "Found " + count + " items in the all directory:");

        return result;
    }

    private static String replacePath(String path, InputArgs inputArgs) {
        if (inputArgs.replaceList == null || inputArgs.replaceList.isEmpty()) {
            return path;
        }
        for (String replace : inputArgs.replaceList) {
            List<Integer> pointList = findReplacePosition(path, replace);
            path = doReplace(path, pointList, getReplacement(replace));
        }
        return path;
    }

    private static String getReplacement(String replace) {
        String regex = "[^" + (File.separator.equals("\\") ? "\\\\" : "/") + ",*]+";
        String[] arr = replace.split(File.separator == "/" ? "/" : "\\\\", Integer.MAX_VALUE);
        StringBuilder replaceSB = new StringBuilder();
        for (int i = 0; i < arr.length; ++i) {
            String s = arr[i];
            if (!s.contains("*")) {
                s = s.replaceAll(regex, "*");
            }
            replaceSB.append(s);
            if (i < arr.length - 1) {
                replaceSB.append(File.separator);
            }
        }
        return replaceSB.toString();
    }

    private static String doReplace(String path, List<Integer> pointList, String replacement) {
        if (!pointList.isEmpty()) {
            int count = pointList.size() / 2;
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < count; i++) {
                if (pointList.get(i * 2) > 0) {
                    sb.append(replacement);
                }
                sb.append(path, pointList.get(i * 2), pointList.get(i * 2 + 1));
            }
            path = sb.toString().replace(File.separator + File.separator, File.separator);
        }
        return path;
    }

    private static List<Integer> findReplacePosition(String path, String replace) {
        List<Integer> pointList = new ArrayList<>();
        int start = 0;
        int lastMatchEnd = 0;

        for (int i = 0, j; i < path.length(); i++) {
            boolean lastStar = false;
            for (j = 0; j < replace.length(); j++) {
                if (i == path.length()) {
                    break;
                }

                char rChar = Character.toLowerCase(replace.charAt(j));
                char pChar = Character.toLowerCase(path.charAt(i));
                if (rChar == '*') {
                    lastStar = true;
                    continue;
                }

                if (rChar != pChar) {
                    if (File.separatorChar == pChar || !lastStar) {
                        break;
                    }
                    --j;
                }

                lastStar = false;
                ++i;
            }
            if (j == replace.length()) {
                if (start > 0) {
                    if (pointList.isEmpty()) {
                        pointList.add(0);
                        pointList.add(start + 1);
                    } else {
                        pointList.add(lastMatchEnd);
                        pointList.add(start + 1);
                    }
                }
                lastMatchEnd = i;
                start = i - 1;
            } else {
                ++start;
            }
            if (start > 0 && replace.startsWith(File.separator) && replace.endsWith(File.separator)) {
                i = start - 1;
            } else {
                i = start;
            }
        }
        if (!pointList.isEmpty()) {
            pointList.add(lastMatchEnd);
            pointList.add(path.length());
        }
        return pointList;
    }

    private static List<String> getFiles(String path, Predicate<File> filter) {
        List<String> fileList = new ArrayList<>();
        Stack<File[]> filesStack = new Stack<>();
        Stack<Integer> indexStack = new Stack<>();

        File[] files = new File[]{new File(path)};
        int index = 0;
        do {
            for (int i = index; i < files.length; i++) {
                File file = files[i];
                if (!file.exists() || (filter != null && !filter.test(file))) {
                    continue;
                }
                if (file.isDirectory()) {
                    File[] listFiles = file.listFiles();
                    if (listFiles == null || listFiles.length == 0) {
                        fileList.add(file.getAbsolutePath());
                    } else {
                        filesStack.push(files);
                        indexStack.push(i + 1);
                        i = -1;
                        files = listFiles;
                    }
                } else {
                    fileList.add(file.getAbsolutePath());
                }
            }
            files = filesStack.pop();
            index = indexStack.pop();
        } while (!filesStack.isEmpty());

        return fileList;
    }

    private static InputArgs parseArgs(String[] args) {
        InputArgs inputArgs = new InputArgs();
        inputArgs.argList = Arrays.asList(args);
        inputArgs.searchMode = inputArgs.argList.contains("--search");
        int length = args.length;

        if (length == 1 && "--help".equalsIgnoreCase(args[0])) {
            System.out.println(HELP_TEXT);
            System.exit(0);
        }

        for (int i = 0; i < length; i++) {
            String arg = args[i];
            if ("--paths".equalsIgnoreCase(arg)) {
                if (inputArgs.pathList == null) {
                    inputArgs.pathList = new ArrayList<>();
                }
                String[] paths = ++i < length ? args[i].split(",") : new String[0];
                inputArgs.pathList.addAll(Arrays.stream(paths).filter(path -> {
                    File file = new File(path);
                    if (!file.exists()) {
                        throw new IllegalArgumentException("Not found file or directory:" + path);
                    }
                    return true;
                }).collect(Collectors.toList()));
            } else if ("--replace".equalsIgnoreCase(arg) || "--search".equalsIgnoreCase(arg)) {
                if (inputArgs.searchMode && "--replace".equalsIgnoreCase(arg)) {
                    continue;
                }
                if (inputArgs.replaceList == null) {
                    inputArgs.replaceList = new ArrayList<>();
                }
                String[] replaces = ++i < length ? args[i].split(",") : new String[0];
                inputArgs.replaceList.addAll(Arrays.stream(replaces)
                        .map(r -> replaceFileSeparator(r, false).replaceAll("[*]+", "*"))
                        .collect(Collectors.toList()));
            } else if ("--exclude".equalsIgnoreCase(arg)) {
                if (inputArgs.excludeList == null) {
                    inputArgs.excludeList = new ArrayList<>();
                }
                String[] pattens = ++i < length ? args[i].split(",") : new String[0];
                inputArgs.excludeList.addAll(getPattenList(pattens));
            } else if ("--outfile".equalsIgnoreCase(arg)) {
                String path = Paths.get(((++i < length) ? args[i] : "")).normalize().toString();
                if (!path.isEmpty()) {
                    inputArgs.outFile = path;
                } else if (inputArgs.outFile == null) {
                    inputArgs.outFile = "";
                }
            } else {
                throw new IllegalArgumentException("Unrecognized option: " + arg);
            }
        }
        if (inputArgs.pathList == null || inputArgs.pathList.isEmpty()) {
            throw new IllegalArgumentException("Invalid paths argument,correct format:--paths <path>[,<path>]..");
        }
        if (inputArgs.replaceList != null && inputArgs.replaceList.isEmpty()) {
            throw new IllegalArgumentException("Invalid replace argument,correct format: --" + (inputArgs.searchMode ? "search" : "replace") + " <str>[,<str>]..");
        }
        if (inputArgs.excludeList != null && inputArgs.excludeList.isEmpty()) {
            throw new IllegalArgumentException("Invalid exclude argument,correct format: --exclude <patten>[,<patten>]..");
        }
        if (inputArgs.outFile != null && inputArgs.outFile.isEmpty()) {
            throw new IllegalArgumentException("Invalid outfile argument,correct format: --outfile str");
        }
        return inputArgs;
    }

    private static Collection<? extends Pattern> getPattenList(String[] pattens) {
        try {
            return Arrays.stream(pattens)
                    .map(patten -> Pattern.compile(replaceFileSeparator(patten, true)))
                    .collect(Collectors.toList());
        } catch (Exception e) {
            throw new IllegalArgumentException("Invalid regular");
        }
    }

    private static String replaceFileSeparator(String str, boolean isRegular) {
        String winSeparator = isRegular ? "\\\\" : "\\";
        if (File.separator.equals("/")) {
            str = str.replace(winSeparator, "/");
        } else {
            str = str.replace("/", winSeparator);
        }
        return str;
    }

}

class InputArgs {
    List<String> argList;
    boolean searchMode;
    List<String> pathList;
    List<String> replaceList;
    List<Pattern> excludeList;
    String outFile;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章