算法-文件系统文件及目录增删改移动

2024-8-8 diaba 算法

package com.jiucaiyuan.net.question;

import java.util.*;

/**
 *  实现一个文件系统
 *  支持文件和目录的增加、删除、重命名、移动
 * 
 * @Author jiucaiyuan  2024/08/08 14:15
 * @mail services@jiucaiyuan.net
 *
 */
public class FileSystem {

    public enum FileType{
        FILE,DIRECTORY
    }

    /**
     * 文件(文件,目录)
     */
    public static class FileNode {
        /**
         * 文件名
         */
        public String name;
        /**
         * 文件类型
         */
        public FileType type;
        /**
         * 父目录
         */
        public FileNode parent;
        /**
         * 当前为目录时所有子文件和目录
         * 不支持重名
         * 文件名或目录名
         * 使用map结构,可以快速查看某个指定名称的文件或目录
         */
        public Map<String,FileNode> childrenMap;

        public FileNode(String fileName,FileType fileType) {
            name = fileName;
            type = fileType;
            parent = null;
            if (fileType == FileType.DIRECTORY){
                childrenMap = new HashMap<>();
            }
        }
    }

    public static class File {
        /**
         * 文件系统的根目录
         */
        private static FileNode root;

        public File() {
            root = new FileNode("/",FileType.DIRECTORY);
        }

        /**
         * 树形输出指定目录下所有文件
         * 暂时格式比较粗糙
         *
         * @param path
         */
        public void tree(String path){
            FileNode current = search(path,null);
            if (current == null ){
                return;
            }
            print(current,0);
        }

        /**
         * 向文件中增加一个文件或目录
         * @param path  目录,格式: "/"、"/User/src"
         * @param fileName
         * @param fileType
         */
        public FileNode create(String path,String fileName,FileType fileType) {
            if (fileName == null || fileType == null) {
                return null;
            }

            FileNode exist = search(path,fileName);
            if (exist != null){
                throw new RuntimeException("文件已存在"+fileName);
            }

            FileNode parentFile = search(path,null);
            if (parentFile == null ){
                throw new RuntimeException("父目录不存在,不能创建该文件");
            }
            FileNode newFile = new FileNode(fileName,fileType);
            newFile.parent = parentFile;
            if (parentFile != null){
                parentFile.childrenMap.put(newFile.name,newFile);
            }
            return newFile;
        }

        /**
         * 删除某个文件或文件夹
         *
         * @param path 删除指定目录下文件或文件夹,如果为空,不允许
         * @param fileName 待删除文件名或目录名
         */
        public boolean delete(String path,String fileName) {
            FileNode needDeleteFile = search(path,fileName);
            if (needDeleteFile == null){
                return false;
            }
            if (needDeleteFile.parent == null){
                // 删除根目录下所有文件
                root.childrenMap = new HashMap<>();
            }else{
                // 删除该文件或目录
                needDeleteFile.parent.childrenMap.remove(fileName);
            }
            return true;
        }

        /**
         * 搜索指定文件
         *
         * 第一版本:查找指定目录下的指定文件或目录(不含子目录下的同名文件或目录)
         * 后期版本再实现指定目录下所有指定文件或目录
         *
         * @param path 搜索指定目录下文件或文件夹,如果为空,搜根目录下
         * @param fileName 待搜索的文件,为空时,表示搜索目录,返回path对应的目录文件
         * @return
         */
        public FileNode search(String path,String fileName) {
            if (path == null) {
                path = "/";
            }

            String[] paths = convertPath(path);

            if (fileName == null){
                if (paths.length == 1){
                    return root;
                }
                fileName = paths[paths.length -1];
                paths = Arrays.copyOfRange(paths, 0, paths.length - 1);
            }

            FileNode node = root;

            if (node.name != paths[0]){
                // 如果根目录不对,直接返回未找到
                return null;
            }else {
                // 如果根目录一致,继续向下查找
                FileNode result = search(node,paths,0,fileName);
                return result;
            }
        }
        /**
         * 移动指定文件到目标目录
         *
         * @param fileName 待移动的文件或目录
         * @param fromPath 从哪里移动
         * @param toPath  移动到哪里
         * @return
         */
        public boolean move(String fileName,String fromPath,String toPath) {

            if (fileName == null){
                return true;
            }
            FileNode fromPathFile = search(fromPath,null);
            if (fromPathFile == null){
                throw new RuntimeException("未找到指定目录 "+fromPath);
            }
            FileNode file = search(fromPath,fileName);
            if (file == null){
                throw new RuntimeException("未找到指定目录下 "+fromPath+" 指定文件 "+fileName);
            }
            FileNode toDirectory = search(toPath,null);
            if (toDirectory == null){
                throw new RuntimeException("未找到指定目标目录 "+fileName);
            }
            toDirectory.childrenMap.put(file.name,file);
            file.parent = toDirectory;
            fromPathFile.childrenMap.remove(fileName);

            return true;
        }

        /**
         * 文件或目录改名
         *
         * @param path 所在目录
         * @param oldName 原名称
         * @param newName  新名称
         * @return
         */
        public void rename(String path, String oldName,String newName) {
            if (oldName == null){
                return;
            }
            FileNode oldFile = search(path,oldName);
            oldFile.parent.childrenMap.remove(oldFile.name);
            oldFile.name = newName;
            oldFile.parent.childrenMap.put(newName,oldFile);
        }

        private FileNode search(FileNode startIndexFileNode,String[] paths,int startIndex,String fileName){

            // 如果节点为空或目录为空,返回没找到
            if (startIndexFileNode == null || paths == null){
                return null;
            }

            // 如果当前已经是末级目录,则只在当前目录下找文件
            if (startIndex == paths.length -1 ){
                if (startIndexFileNode.type == FileType.FILE){
                    return null;
                }
                for (String key : startIndexFileNode.childrenMap.keySet()){
                    FileNode childFileNode = startIndexFileNode.childrenMap.get(key);
                    if ( childFileNode.name.equals(fileName)) {
                        return childFileNode;
                    }
                }
                return null;
            }else
            // 如果当前非末级目录,需要找下一级目录
            if (startIndexFileNode.type == FileType.DIRECTORY && paths[startIndex].equals(startIndexFileNode.name)){
                for (String key : startIndexFileNode.childrenMap.keySet()){
                    FileNode childFileNode = startIndexFileNode.childrenMap.get(key);
                    if (childFileNode.type == FileType.DIRECTORY ){
                        // 在下一级目录中继续找
                        FileNode dest = search(childFileNode,paths,startIndex +1,fileName);
                        if (dest != null) {
                            return dest;
                        }
                    }
                }
            }
            return null;
        }

        private String[] convertPath(String path){
            if (path == null) {
                return new String[]{};
            }
            String old = path;
            if (path.startsWith("/")){
                path = path.substring(1);
            }
            if (path.endsWith("/")){
                path = path.substring(0,path.length());
            }
            String[] paths = path.split("/");
            List<String> pathList = new ArrayList<>();
            pathList.add("/");
            for (int i=0;i<paths.length;i++){
                if (paths[i].equals("")){
                    continue;
                }
                pathList.add( paths[i]);
            }
            String[] result = pathList.toArray(new String[pathList.size()]);
            return result;
        }

        private void print(FileNode node,int level){
            if (node == null) {
                System.out.println("");
            }else{
                if (level > 0) {
                    System.out.println(formatPrefix(level) + node.name);
                }else {
                    System.out.println(".");
                }
            }
            if (node.type == FileType.DIRECTORY ){
                for (String key : node.childrenMap.keySet()){
                    FileNode value = node.childrenMap.get(key);
                    print(value,level + 1);
                }
            }
        }

        private String formatPrefix(int count){
            if( count == 0){
            }
            StringBuffer sb = new StringBuffer();
            for (int i = 0;i<count;i++){
                if (i == count -1 ){
                    sb = sb.append("├─");
                }else{
                    sb = sb.append("  ");
                }
            }
            return sb.toString();
        }
    }



    public static void main(String[] args){
        FileSystem.File file = new FileSystem.File();
        // 创建目录和文件
        file.create(null,"readme.txt",FileType.FILE);
        file.create("/","A",FileType.DIRECTORY);
        file.create("/A","AB",FileType.DIRECTORY);
        file.create("/A/AB","AC",FileType.DIRECTORY);
        file.create("/A/AB/AC","AD",FileType.DIRECTORY);
        file.create("/A/AB/AC/AD","AD.txt",FileType.DIRECTORY);
        file.create("/A","AE",FileType.DIRECTORY);
        file.create("/A","AF",FileType.DIRECTORY);
        file.create("/A","AG",FileType.DIRECTORY);
        file.create("/A","AG.txt",FileType.DIRECTORY);
        file.create("/","B",FileType.DIRECTORY);
        file.create("/B/","BB",FileType.DIRECTORY);
        System.out.println("打印根目录");
        file.tree("/");
        // 删除目录
        file.delete("/A/AB/AC","AD");
        System.out.println("删除后打印根目录");
        file.tree("/");

        // 移动文件到指定目录下
        file.move("readme.txt","/","/A/AB/AC");
        System.out.println("移动后打印根目录");
        file.tree("/");

        // 移动目录到指定目录下
        file.move("AB","/A","/B/BB");
        System.out.println("移动后打印根目录");
        file.tree("/");

        // 查询指定目录下的文件
        FileNode destFile = file.search("/B/BB/AC","readme.txt");
        System.out.println("查找目录 /B/BB/AC 下的 readme.txt 文件,结果 file = " + destFile);

        // 查询指定目录下的文件
        FileNode destFile2 = file.search("/B/BB/AB/AC","readme.txt");
        System.out.println("查找目录 /B/BB/AC 下的 readme.txt 文件,结果 file = " + destFile2);

    }

}

发表评论:

Powered by emlog 京ICP备15045175号-1 Copyright © 2022