在一篇博客基础上,做了大幅度改动,时间仓促,代码冗余较多,耦合度高。仅供参考。
参考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 subMap = new HashMap();
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 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 getBlocks(int startNum) {
List 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> iterator = fileModel.subMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry 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();
}
}