【文件管理】操作系统简易文件管理系统
前言:
在一篇博客基础上,做了大幅度改动,时间仓促,代码冗余较多,耦合度高。仅供参考。
参考blog: 磁盘文件管理
指令集:
ls & ll 列出文件/目录 touch [文件名]: 创建文件 echo [内容] > [文件名] 覆盖 echo [内容] >> [文件名] 追加 cd [相对路径/绝对路径] : 进入某个目录 cd .. : 返回上一级 mkdir [相对路径/绝对路径] : 创建目录 mkdir -p [相对路径/绝对路径] : 创建多级目录 rm [路径] : 删除文件 rm -r [路径] : 删除目录文件或者文件 exit: 退出
运行截图:
代码:
1.文件/目录的实体类:
import java.io.Serializable; import java.util.HashMap; import java.util.Map; public class FileModel implements Serializable { public Map<String, FileModel> subMap = new HashMap<String, FileModel>(); private String name; //文件名或目录名 private int attr; //用来识别是文件还是目录 private int startNum; //在FAT表中起始位置 private int size; //文件的大小 private String content;//只有文件才能有这个字段 private FileModel father = null; //该文件或目录的上级目录 public FileModel(String name){ this.name = name; } public FileModel(String name, int startNum, int size) { this.name = name; this.attr = 2; this.startNum = startNum; this.size = size; } public FileModel(String name, int startNum) { this.name = name; this.attr = 3; this.startNum = startNum; this.size = 0; } //getter,setter方法... }
2.文件管理:
import com.system.utils.OSUtils; import com.system.utils.Utils; import com.system.entity.FileModel; import java.util.*; public class OSManager { private static int TOTAL_BLOCKS = 128; //定义FAT表 private static int[] fat = new int[TOTAL_BLOCKS]; //创建根目录(名字,在fat表中的起始位置) private FileModel root = new FileModel("\\", 1); //当前打开的目录 private FileModel nowCatalog = root; //磁盘块能够存放的字节数 private static int BLOCK_BIT = 10;//假设一个磁盘块容纳的字节数量 //初始化 public OSManager() { //初始化fat表 //将第一位设置为跟文件的空间 Arrays.fill(fat, 0); fat[1] = -1;//代表这块磁盘已经被占用 fat[0] = 126;//第0块代表有多少是空闲的 root.setFather(null); } /** * 创建文件 * * @param name 文件名 * @param size 大小 */ public void createFile(String name, int size) { //判断磁盘空间是否足够 if (fat[0] >= size) { //在当前目录下查找是否有同名目录或者文件 FileModel fileModel = nowCatalog.subMap.get(name); if (fileModel == null) {//文件存在 //如果当前目录下面没有这个文件 那么继续创建即可 //分配磁盘块 int startNum = setFat(size); FileModel newFile = new FileModel(name, startNum, size); //记录他的父级 newFile.setFather(nowCatalog); //父级增加 FileModel father = newFile.getFather(); while (father != root) { father.setSize(father.getSize() + size); father = father.getFather(); } //在目录下添加该文件 nowCatalog.subMap.put(name, newFile); fat[0] -= size; System.out.println("文件创建成功"); } else { System.out.println("创建失败,文件已经存在!"); } } else { System.out.println("创建文件失败,磁盘空间不足!"); } } /** * 创建层级目录 * * @param start 如果是绝对路径创建,那么start为root;如果相对路径创建,那么start为当前目录 * @param name 假设创建/usr/local 那么name为{“usr”,"local"} */ public void createFileOrCatalog(FileModel start, String[] name) { int len = name.length; if (fat[0] >= len) { FileModel cur = start; for (int i = 0; i < name.length; i++) { if (!cur.subMap.containsKey(name[i])) {//如果存在 那么不需要再创建 FileModel fileModel = new FileModel(name[i]); fileModel.setAttr(3); cur.subMap.put(name[i], fileModel); fileModel.setFather(cur); } cur = cur.subMap.get(name[i]); } } else { System.out.println("创建目录失败,磁盘空间不足!"); } } public void showBreadLine() { //面包线 FileModel cur = nowCatalog; Deque<String> deque = new LinkedList<>(); deque.push(">"); if (cur != root) { while (cur != root) { deque.push(cur.getName()); deque.push("\\"); cur = cur.getFather(); } } else { deque.push("\\"); } deque.push("C:"); StringBuilder sb = new StringBuilder(); while (!deque.isEmpty()) { sb.append(deque.pop()); } System.out.print(sb.toString()); } //显示目下所有的文件信息 //C:\root> public void showFiles() { System.out.println("-----------------------------------------------------------------------------"); //文件状态 if (!nowCatalog.subMap.isEmpty()) { System.out.printf("%-8s %-8s %-8s %-8s %-12s\n", "文件名", "操作类型", "起始盘块", "大小(块)", "磁盘号(块)"); for (FileModel model : nowCatalog.subMap.values()) { if (model.getAttr() == 2) { String blocks = getBlocks(model.getStartNum()).toString(); System.out.printf("%-8s %-9s %-12d %-8d %-12s\n", model.getName(), "可读可写文件", model.getStartNum(), model.getSize(), blocks.substring(1, blocks.length() - 1)); } else if (model.getAttr() == 3) { System.out.printf("%-8s %-9s\t %-12s %-8d %-12s\n", model.getName(), "目录", "---", model.getSize(), "---"); } } } else { System.out.println("当前目录下没有文件!"); } //磁盘空闲信息 System.out.println("-----------------------------------------------------------------------------"); System.out.printf("%-10s\t%-10s\t%-10s\n", "磁盘总容量(块)", "占用率", "剩余容量(块)"); System.out.printf("%-10s\t\t%-10s\t\t%-10s\n", TOTAL_BLOCKS, Utils.d2s((TOTAL_BLOCKS - fat[0]) * 1.0 / TOTAL_BLOCKS), fat[0]); System.out.println("-----------------------------------------------------------------------------"); } public List<Integer> getBlocks(int startNum) { List<Integer> ans = new ArrayList<>(); int cur = startNum; while (fat[cur] != -1) { ans.add(cur); cur = fat[cur]; } ans.add(cur); return ans; } private int isDir(FileModel start, String[] names) { FileModel cur = start; for (int i = 0; i < names.length; i++) { if (cur.getAttr() == 3 && cur.subMap.containsKey(names[i])) { cur = cur.subMap.get(names[i]); } else { return -2;//表示文件不存在 } } if (cur.getAttr() == 3) {//文件夹 return 1; } return -1;//文件 } /* 递归删除某个目录以及下面的内容 */ public void deleteCatalog(FileModel start, String[] path) { FileModel cur = start; for (int i = 0; i < path.length; i++) { if (cur.subMap.containsKey(path[i])) { cur = cur.subMap.get(path[i]); } else { System.out.println("删除失败,目录不存在!"); return; } } cur.getFather().subMap.remove(cur.getName());//将其从父级目录中删除 if (cur.getAttr() == 3) {//文件夹 delDfs(cur); } else { delFat(cur.getStartNum());//文件 fat[0] += cur.getSize(); FileModel father = cur.getFather(); while (father != null) { father.setSize(father.getSize() - cur.getSize()); father = father.getFather(); } } cur.subMap = null; } private void delDfs(FileModel fileModel) { Iterator<Map.Entry<String, FileModel>> iterator = fileModel.subMap.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, FileModel> entry = iterator.next(); if (entry.getValue().getAttr() == 3) { delDfs(entry.getValue()); } else {//如果是文件 fat[0] += entry.getValue().getSize(); delFat(entry.getValue().getStartNum());//回收文件空间 FileModel father = entry.getValue().getFather(); while (father != root) { father.setSize(father.getSize() - entry.getValue().getSize()); father = father.getFather(); } } } } /* 对文件进行重命名 */ public void reName(String name, String newName) { if (nowCatalog.subMap.containsKey(name)) { if (!nowCatalog.subMap.containsKey(newName)) { //获取原文件 FileModel fileModel = nowCatalog.subMap.get(name); fileModel.setName(newName); nowCatalog.subMap.remove(name); nowCatalog.subMap.put(newName, fileModel); System.out.println("重命名成功"); } else { System.out.println("重命名失败,同名文件已经存在"); } } else { System.out.println("重命名失败,没有该文件"); } } //假设我要创建 mkdir -p /find/usr/local[usr不存在的情况] mkdir /find/usr/local [usr存在] //使用-p还是不使用取决于我创建的是否是多级目录 private boolean canCreate(FileModel start, String[] path, int len) { FileModel cur = start; for (int i = 0; i < len; i++) { if (!cur.subMap.containsKey(path[i])) { return false; } else if (cur.subMap.get(path[i]).getAttr() == 2) {//如果包含 但是是文件类型 这种情况直接创建失败 return false; } cur = cur.subMap.get(path[i]); } return true; } /* 返回上级目录 */ public void backFile() { if (nowCatalog.getFather() == null) { System.out.println("已经是顶级目录"); } else { nowCatalog = nowCatalog.getFather(); } } /* 通过绝对路径的方式找到文件 /user/local/bin 将其分解为 */ public void openCatalog(FileModel start, String[] loadName) { //从根向下查找 FileModel cur = start; for (int i = 0; i < loadName.length; i++) { if (cur.subMap.containsKey(loadName[i])) { FileModel fileModel = cur.subMap.get(loadName[i]); if (fileModel.getAttr() == 3) { cur = fileModel; } else { System.out.println("目录不存在"); return; } } else { System.out.println("目录不存在"); return; } } nowCatalog = cur; } //cat 文件名 public void showContent(String name) { if (nowCatalog.subMap.containsKey(name)) { FileModel fileModel = nowCatalog.subMap.get(name); if (fileModel.getAttr() == 2) { System.out.println(fileModel.getContent()); } else { System.out.println(name + "是一个目录!"); } } else { System.out.println("文件不存在"); } } /** * echo指令 * 对内容进行编辑 type = 0,使用追加的方式 type = 1使用修改的方式 * * @param name * @param type */ public void updateContent(String name, int type, String content) { if (nowCatalog.subMap.containsKey(name)) { FileModel fileModel = nowCatalog.subMap.get(name); if (fileModel.getAttr() == 2) { if (type == 0) { //当前能够容纳的字节数量 int hasBits = fileModel.getSize() * BLOCK_BIT; //需要容纳的字节数量 int needBits = (fileModel.getContent() + content).getBytes().length; if (needBits > hasBits) { //开启磁盘 int addSize = (int) Math.ceil((needBits - hasBits) * 1.0 / BLOCK_BIT); if (reAddFat(fileModel.getStartNum(), addSize) >= 0) {//如果追加成功 fat[0] -= addSize; FileModel father = fileModel.getFather(); while (father != root) { father.setSize(father.getSize() + addSize); father = father.getFather(); } fileModel.setSize(fileModel.getSize() + addSize); fileModel.setContent(fileModel.getContent() + content); } else { System.out.println("磁盘空间不足,写文件失败!"); } } else { fileModel.setContent(fileModel.getContent() + content); } } else if (type == 1) { int hasBits = fileModel.getSize() * BLOCK_BIT; int needBits = content.getBytes().length; if (needBits > hasBits) { int addSize = (int) Math.ceil((needBits - hasBits) * 1.0 / BLOCK_BIT); if (reAddFat(fileModel.getStartNum(), addSize) >= 0) { fat[0] -= addSize; FileModel father = fileModel.getFather(); while (father != root) { father.setSize(father.getSize() + addSize); father = father.getFather(); } fileModel.setSize(fileModel.getSize() + addSize); fileModel.setContent(content); } else { System.out.println("磁盘空间不足,写文件失败!"); } } else { fileModel.setContent(content); } } } else { System.out.println(name + "是一个目录!"); } } else { System.out.println("文件不存在!"); } } //关于fat的一些操作 //分配 返回的是起始块号 public static int setFat(int size) { int pre = -1; int startNum = pre; int count = 0; for (int i = 2; i < fat.length; i++) { if (fat[i] == 0) {//如果当前是空闲块 if (pre != -1) { fat[pre] = i; pre = i; } else { startNum = i; pre = i; } count++; if (count == size) { fat[i] = -1;//设置为文件的最后一块 break; } } } return startNum; } /* 删除某个文件 */ public static void delFat(int startNum) { int cur = startNum; while (fat[cur] != -1) { //将这个位置设置为0 int tmp = cur; cur = fat[cur]; fat[tmp] = 0; } //将最后的设置为0 fat[cur] = 0; } /* 追加多少块 */ public static int reAddFat(int startNum, int addSize) { if (fat[0] >= addSize) { int curNum = startNum; while (fat[curNum] != -1) { curNum = fat[curNum]; } int freeNum = setFat(addSize); fat[curNum] = freeNum; return 1; } return -1; } public void run() { //加载数据 root = OSUtils.read(); fat = OSUtils.read2(); nowCatalog = root; Scanner scanner = new Scanner(System.in); boolean loop = true; while (loop) { showBreadLine(); //对指令进行处理 String[] split = scanner.nextLine().split(" "); String commend = split[0]; if (commend.equals("cd")) {//这里需要区分使用的绝对路径还是相对路径 String dirName = split[1]; if ("..".equals(dirName)) { backFile(); } else if (dirName.startsWith("/")) { //绝对路径 例如/usr/local/bin 想要查找的是绝对路径 if ("/".equals(dirName)) { openCatalog(this.root, new String[0]); } else { String[] filePath = dirName.substring(1).split("/"); openCatalog(this.root, filePath); } } else { //如果起始不是/ 相当于查找的是相对路径 String[] filePath = dirName.split("/"); openCatalog(this.nowCatalog, filePath); } } else if (commend.equals("mkdir")) { String dirName = ""; if (split.length == 2) { dirName = split[1]; if (dirName.startsWith("/")) { String[] path = dirName.substring(1).split("/"); boolean canCreate = canCreate(this.root, path, path.length - 1); if (canCreate) { createFileOrCatalog(this.root, path); } else { System.out.println("创建失败!"); } } else { String[] path = dirName.split("/"); boolean canCreate = canCreate(this.nowCatalog, path, path.length - 1); if (canCreate) { createFileOrCatalog(this.nowCatalog, path); } else { System.out.println("创建失败!"); } } } else if (split.length == 3) { dirName = split[2]; if (dirName.startsWith("/")) { createFileOrCatalog(this.root, dirName.substring(1).split("/")); } else { createFileOrCatalog(this.nowCatalog, dirName.split("/")); } } } else if (commend.equals("touch")) { String fileName = split[1]; createFile(fileName, 1); } else if (commend.equals("cat")) { String fileName = split[1]; showContent(fileName); } else if (commend.equals("ls") || commend.equals("ll")) { showFiles(); } else if (commend.equals("mv")) { String oldName = split[1]; String newName = split[2]; reName(oldName, newName); } else if (commend.equals("echo")) { String content = split[1]; String type = split[2]; String fileName = split[3]; if (">>".equals(type)) {//追加 updateContent(fileName, 0, content); } else {//覆盖 updateContent(fileName, 1, content); } } else if (commend.equals("rm")) { if (split.length == 2) {//rm XXX 删除文件 //是相对路径 还是绝对路径 String name = split[1]; if (name.startsWith("/")) {//绝对路径 /usr/local/aa.txt int isDir = isDir(this.root, name.substring(1).split("/")); if (isDir == -2) { System.out.println("文件不存在!"); } else if (isDir == 1) { System.out.println("文件夹不能删除!"); } else { deleteCatalog(this.root, name.substring(1).split("/")); } } else { int isDir = isDir(this.nowCatalog, name.split("/")); if (isDir == -2) { System.out.println("文件不存在!"); } else if (isDir == 1) { System.out.println("文件夹不能删除!"); } else { deleteCatalog(this.nowCatalog, name.split("/")); } } } else if (split.length == 3) { String name = split[2]; if (name.startsWith("/")) { deleteCatalog(this.root, name.substring(1).split("/")); } else { deleteCatalog(this.nowCatalog, name.split("/")); } } } else if (commend.equals("exit")) { loop = false; //持久化 OSUtils.write(root); OSUtils.write2(fat); } else { System.out.println("指令不存在"); } } } }
3.读写文件工具类(在项目启动的时候,将dat文件中的数据加载到程序中,项目终止时持久化到dat文件中)
import java.io.*; public class OSUtils { public static void write(FileModel root) { try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("file-system/src/model.dat"))) { oos.writeObject(root); oos.flush(); } catch (IOException e) { e.printStackTrace(); } } //从磁盘读取出来 public static FileModel read() { try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("file-system/src/model.dat"))) { FileModel fileModel = (FileModel) ois.readObject(); return fileModel; } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } return null; } //将数组写入文件 public static void write2(int[] array) { try (BufferedWriter writer = new BufferedWriter(new FileWriter("file-system/src/os.dat"))) { String line = ""; for (int i = 0; i < array.length; i++) { line += array[i]; line += " "; } writer.write(line); } catch (IOException e) { e.printStackTrace(); } } public static int[] read2() { int[] arr = new int[128]; int index = 0; try (BufferedReader reader = new BufferedReader(new FileReader("file-system/src/os.dat"))) { String[] split = reader.readLine().split(" "); for (String s : split) { arr[index++] = Integer.parseInt(s); } } catch (IOException e) { e.printStackTrace(); } return arr; } }
4.启动类
import com.system.service.OSManager; public class Main { public static void main(String[] args) { new OSManager().run(); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理