1. 程式人生 > >作業系統課程設計 —— 模擬磁碟檔案系統實現 (Java)

作業系統課程設計 —— 模擬磁碟檔案系統實現 (Java)

這是我前段時間做了一個作業系統課程設計作業,使用java實現了命令列輸入對虛擬檔案進行管理。

下面是課程設計要求:

題目五  模擬磁碟檔案系統實現 
一、課程設計目的 
瞭解磁碟檔案系統的結構、功能和實現。並可練習合作完成系統的團隊精神和提高
程式設計能力。 
二、小組人數 建議 3~5 人一組共同完成模擬磁碟檔案系統的實現。 選擇題目“模擬磁碟檔案系統實現”的小組在最終提交時須公開演示及講解。 由於這個題目較複雜,難度和工作量遠大於前面幾個題目,故小組成員最後得分 也酌情高於選擇前面四個題目的同學的分數(高 5~10 分)。 三、程式語言 
建議使用一些 Windows 環境下的程式設計語言如 VC、Java,以藉助這些語言的多
執行緒來模擬並行發生的行為。要求圖形介面。 
四、課程設計內容 
設計一個簡單的檔案系統,用檔案模擬磁碟,用陣列模擬緩衝區,要求: 
(1) 支援多級目錄結構,支援檔案的絕對讀路徑; 
(2) 檔案的邏輯結構採用流式結構,物理結構採用連結結構中的顯式連結方式; 
(3) 採用檔案分配表 FAT; 
(4) 實現的命令包括建立目錄、列目錄、刪除空目錄、建立檔案、刪除檔案、顯示
檔案內容、開啟檔案、讀檔案、寫檔案、關閉檔案、改變檔案屬性。可以採用
命令列介面執行這些命令,也可以採用“右擊快捷選單選擇”方式執行命令。 
(5) 後編寫主函式對所作工作進行測試。 



下面是我實現的功能:




以下為原始碼

首先是一個FileModel類用來記錄檔案或目錄的相關屬性

package com.model;

import java.util.ArrayList;
import java.util.HashMap;

import java.util.Map;

public class FileModel {
	
	public Map<String, FileModel> subMap = new HashMap<String, FileModel>();
	private String name; //檔名或目錄名
	private	String type; //檔案型別
	private int attr; //用來識別是檔案還是目錄 
	private int startNum;	//在FAT表中起始位置
	private int size;	//檔案的大小
	private FileModel father = null;	//該檔案或目錄的上級目錄
	
	public FileModel(String name, String type, int startNum, int size){
		this.name = name;
		this.type = type;
		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.type = "  ";
		this.size = 1;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getType() {
		return type;
	}
	public void setType(String type) {
		this.type = type;
	}
	public int getAttr() {
		return attr;
	}
	public void setAttr(int attr) {
		this.attr = attr;
	}
	public int getStartNum() {
		return startNum;
	}
	public void setStartNum(int startNum) {
		this.startNum = startNum;
	}
	public int getSize() {
		return size;
	}
	public void setSize(int size) {
		this.size = size;
	}

	public FileModel getFather() {
		return father;
	}

	public void setFather(FileModel father) {
		this.father = father;
	}
	
}
接著使用OSManager這個類實現對檔案的各種操作
package com.service;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import com.model.FileModel;
import com.sun.xml.internal.bind.v2.util.FatalAdapter;

public class OSManager {
	
	public Map<String, FileModel> totalFiles = new HashMap<String, FileModel>();
	//定義FAT表
	private int[] fat = new int[128]; 
	//建立根目錄 使用fat表的第一項
	private FileModel root = new FileModel("root", 1);
	private FileModel nowCatalog = root;


	
	public OSManager() {
		// TODO Auto-generated consructor stub
		//將FAT表初始化全部為0,並將第一位設為根目錄的空間
		for(int i=0; i<fat.length ; i++ ) {
			fat[i] = 0;
		}
		fat[1] = 255; //255表示磁碟塊已佔用
		fat[0] = 126; //紀錄磁碟剩餘塊數	
		root.setFather(root);
		totalFiles.put("root", root);
	}
	
	public int setFat(int size) {
		int[] startNum = new int[128];
		int i = 2; //紀錄fat迴圈定位
		for(int j=0; j<size; i++) {
			if(fat[i] == 0) {
				startNum[j] = i; //紀錄該檔案所有磁碟塊
				if(j>0) {
					fat[startNum[j-1]] = i; //fat上一磁碟塊指向下一磁碟塊地址
				}
				j++;
			}
		}
		fat[i-1] = 255;
		return startNum[0]; //返回該檔案起始塊盤號
	}
	/*
	 * 
	 * 該方法用於刪除時釋放FAT表的空間
	 */
	public void delFat(int startNum) {
		int nextPoint = fat[startNum];
		int nowPoint = startNum;
		int count = 0;
		while(fat[nowPoint] != 0) {
			nextPoint = fat[nowPoint];
			if(nextPoint == 255) {
				fat[nowPoint] =0;
				count++;
				break;
			} else {
				fat[nowPoint] = 0;
				count++;
				nowPoint = nextPoint;
			}
		}
		fat[0] += count;
	}
	
	/*
	 * 
	 * 以下為追加內容時修改fat表
	 * 
	 */
	
	public void reAddFat(int startNum, int addSize) {
		int nowPoint = startNum;
		int nextPoint = fat[startNum];
		while(fat[nowPoint] != 255) {
			nowPoint = nextPoint;
			nextPoint = fat[nowPoint];
		}//找到該檔案終結盤塊

		for(int i=2, count = 0; count <addSize ; i++ ) {
			if(fat[i] == 0) {
				fat[nowPoint] = i;
				nowPoint = i;
				count++;
				fat[nowPoint] = 255;//作為當前檔案終結盤塊
			}
		}
	}
	
	/*
	 * 	以下為建立檔案和目錄方法
	 * 	14R5黎志亮  
	 */
	public void createFile(String name, String type, int size) {
		
		if(fat[0] >= size) {	//判斷磁碟剩餘空間是否足夠建立檔案
			FileModel value = nowCatalog.subMap.get(name); //該目錄下是否尋找同名目錄或檔案
			if(value != null) {  //判斷該檔案是否存在
				if(value.getAttr() == 3) {   //若存在同名目錄 繼續建立檔案
					int startNum = setFat(size); 
					FileModel file = new FileModel(name, type, startNum, size);
					file.setFather(nowCatalog); //紀錄上一層目錄
					nowCatalog.subMap.put(name, file); //在父目錄新增該檔案
					totalFiles.put(file.getName(), file);
					fat[0] -= size;
					System.out.println("建立檔案成功!");
					showFile();
				} else if(value.getAttr() == 2) { //若同名檔案已存在,建立失敗
					System.out.println("建立失敗,該檔案已存在!"); 
					showFile();
				}
			} else if(value == null) { //若無同名檔案或資料夾,繼續建立檔案
				int startNum = setFat(size); 
				FileModel file = new FileModel(name, type, startNum, size);
				file.setFather(nowCatalog); //紀錄上一層目錄
				nowCatalog.subMap.put(name, file); //在父目錄新增該檔案
				totalFiles.put(file.getName(), file);
				fat[0] -= size;
				System.out.println("建立檔案成功!");
				showFile();
				}
		} else {
			System.out.println("建立檔案失敗,磁碟空間不足!");
		}
	
	}
	
	public void createCatolog(String name) {
		
		if(fat[0] >= 1) { //判斷磁碟空間是否足夠建立資料夾
			
			FileModel value = nowCatalog.subMap.get(name); //判斷該目錄下是否存在同名目錄或檔案
			if(value != null) {
				if(value.getAttr() == 2) {
					int startNum = setFat(1);
					FileModel catalog = new FileModel(name, startNum);
					catalog.setFather(nowCatalog); //紀錄上一層目錄
					nowCatalog.subMap.put(name, catalog);
					fat[0]--;
					totalFiles.put(catalog.getName(), catalog);
					System.out.println("建立目錄成功!");
					showFile();
				} else if(value.getAttr() == 3) {
					System.out.println("建立目錄失敗,該目錄已存在!");
					showFile();
				} 
			} else if(value == null) {
				int startNum = setFat(1);
				FileModel catalog = new FileModel(name, startNum);
				catalog.setFather(nowCatalog); //紀錄上一層目錄
				nowCatalog.subMap.put(name, catalog);
				fat[0]--;
				totalFiles.put(catalog.getName(), catalog);
				System.out.println("建立目錄成功!");
				showFile();
			}			
		} else {
			System.out.println("建立目錄失敗,磁碟空間不足!");
		}
	}
	
	
	/*
	 * 
	 * 以下為顯示該目錄下的所有檔案資訊
	 * 
	 */
	 
	public void showFile() {
		System.out.println("***************** < " + nowCatalog.getName() + " > *****************");
       
		if(!nowCatalog.subMap.isEmpty()) {
			for(FileModel value : nowCatalog.subMap.values()) {
				if(value.getAttr() == 3) { //目錄檔案
					System.out.println("檔名 : " + value.getName());
					System.out.println("操作型別 : " + "資料夾");
					System.out.println("起始盤塊 : " + value.getStartNum());
					System.out.println("大小 : " + value.getSize());
					System.out.println("<-------------------------------------->");
				}
				else if(value.getAttr() == 2) {
					System.out.println("檔名 : " + value.getName() + "." + value.getType());
					System.out.println("操作型別 : " + "可讀可寫檔案");
					System.out.println("起始盤塊 : " + value.getStartNum());
					System.out.println("大小 : " + value.getSize());
					System.out.println("<-------------------------------------->");
				}
			}
		}
		for(int i =0; i<2; i++)	
		System.out.println();
		System.out.println("磁碟剩餘空間 :" + fat[0] + "            " + "退出系統請輸入exit");
		System.out.println();
	}
	/*
	 * 
	 * 以下為刪除該目錄下某個檔案
	 * 
	 */
	public void deleteFile(String name) {
		
		FileModel value = nowCatalog.subMap.get(name);
		if(value == null) {
			System.out.println("刪除失敗,沒有該檔案或資料夾!");
		} else if(!value.subMap.isEmpty()) {
			System.out.println("刪除失敗,該資料夾內含有檔案!");
		} else {
			nowCatalog.subMap.remove(name);
			delFat(value.getStartNum());
			if(value.getAttr() == 3) {
				System.out.println("資料夾 " + value.getName() + " 已成功刪除");
				showFile();
			} else if(value.getAttr() == 2) {
				System.out.println("檔案 " + value.getName() + "已成功刪除");
				showFile();
			}
		}
	}
	
	/*
	 * 
	 * 以下為檔案或資料夾重新命名方法
	 * 
	 */
	
	public void reName(String name, String newName) {
		if(nowCatalog.subMap.containsKey(name)) {
			if(nowCatalog.subMap.containsKey(newName)) {
				System.out.println("重新命名失敗,同名檔案已存在!");	
				showFile();
			} else {
				//nowCatalog.subMap.get(name).setName(newName);
				FileModel value = nowCatalog.subMap.get(name);
				value.setName(newName);
				nowCatalog.subMap.remove(name);
				nowCatalog.subMap.put(newName, value);
				System.out.println("重新命名成功!");
				System.out.println();
				showFile();
			}
		} else {
			System.out.println("重新命名失敗,沒有該檔案!");
			showFile();
		}
	}
	
	/*
	 * 
	 * 以下為修改檔案型別
	 * 修改型別需要開啟檔案後才能操作
	 */
	
	public void changeType(String name, String type) {
		
		nowCatalog = nowCatalog.getFather();
		if(nowCatalog.subMap.containsKey(name)) {
			FileModel value = nowCatalog.subMap.get(name);
			if(value.getAttr() == 2) {
				value.setType(type);
				nowCatalog.subMap.remove(name);
				nowCatalog.subMap.put(name, value);
				System.out.println("修改型別成功!");
				showFile();
			} else if(value.getAttr() == 3) {
				System.out.println("修改錯誤,資料夾無法修改型別!");
				openFile(value.getName());
			}
		} else {
			System.out.println("修改錯誤,請檢查輸入檔名是否正確!");
		}
	}
	
	/*
	 * 以下為開啟檔案或資料夾方法
	 * 
	 */
	
	public void openFile(String name) {
		if(nowCatalog.subMap.containsKey(name)) {
			FileModel value = nowCatalog.subMap.get(name);
			if(value.getAttr() == 2) {
				nowCatalog = value;
				System.out.println("檔案已開啟,檔案大小為 : " + value.getSize());				
			} else if(value.getAttr() == 3) {
				nowCatalog = value;
				System.out.println("資料夾已開啟!");
				showFile();
			}
		} else {
			System.out.println("開啟失敗,檔案不存在!");
		}
	}
	
	/*
	 * 
	 * 以下為向檔案追加內容方法
	 * 追加內容需要開啟檔案後才能操作
	 */
	
	public void reAdd(String name, int addSize) {
		
		if(fat[0] >= addSize) {
			nowCatalog = nowCatalog.getFather();
			if(nowCatalog.subMap.containsKey(name)) {
				FileModel value = nowCatalog.subMap.get(name);
				if(value.getAttr() == 2) {
					value.setSize(value.getSize() + addSize);
					reAddFat(value.getStartNum(), addSize);
					System.out.println("追加內容成功!正在重新開啟檔案...");
					openFile(name);
				} else{
					System.out.println("追加內容失敗,請確認檔名是否正確輸入。");					
				}
			} else {
				System.out.println("追加內容失敗,請確認檔名是否正確輸入!");
				showFile();
			}
		} else {
			System.out.println("追加內容失敗,記憶體空間不足!");
		}
	}
	
	
	/*
	 * 
	 * 以下為返回上一層目錄
	 * 
	 */
	
	public void backFile() {
		if(nowCatalog.getFather() == null) {
			System.out.println("該檔案沒有上級目錄!");
		} else {
			nowCatalog = nowCatalog.getFather();
			showFile();
		}
	}
	
	/*
	 * 以下根據絕對路徑尋找檔案
	 * 
	 */
	
	public void searchFile(String[] roadName) {
		
		FileModel theCatalog = nowCatalog; //設定斷點紀錄當前目錄
		
		if(totalFiles.containsKey(roadName[roadName.length-1])) { //檢查所有檔案中有無該檔案

			nowCatalog = root; //返回根目錄
			if(nowCatalog.getName().equals(roadName[0])) {	//判斷輸入路徑的首目錄是否root
				System.out.println("yes");
				/*for(String temp:roadName)
					System.out.println(temp);*/
				
				for(int i=1; i<roadName.length; i++) {
					if(nowCatalog.subMap.containsKey(roadName[i])) {
						/*System.out.println(nowCatalog.getName());
						for(Entry<String, FileModel> s : nowCatalog.subMap.entrySet())
							System.out.println("鍵值對:" + s.getValue().getName());
							*/
						nowCatalog = nowCatalog.subMap.get(roadName[i]); //一級一級往下查

					} else {
						System.out.println("找不到該路徑下的檔案或目錄,請檢查路徑是否正確");
						nowCatalog = theCatalog;
						showFile();
						break;
					}
				}
				if(roadName.length>1){
					nowCatalog = nowCatalog.getFather(); //返回檔案上一級目錄 
					showFile();
				}
			} else {
				nowCatalog = theCatalog;
				System.out.println("請輸入正確的絕對路徑!");
				showFile();
			}
		} else {
			System.out.println("該檔案或目錄不存在,請輸入正確的絕對路徑!");
			showFile();
		}
	}
	
	/*
	 * 以下為列印FAT表內容
	 * 
	 */
	public void showFAT() {

		for(int j=0; j<125; j+=5) {
			System.out.println("第幾項 | " + j + "        " + (j+1) + "        " + (j+2) + "        "
					+ (j+3) + "        " + (j+4));
			System.out.println("內容    | " + fat[j] + "        " + fat[j+1] + "        " + fat[j+2]
					 + "        " + fat[j+3] + "        " + fat[j+4]);
			System.out.println();
		}
		int j = 125;
		System.out.println("第幾項 | " + j + "        " + (j+1) + "        " + (j+2));
		System.out.println("內容    | " + fat[j] + "        " + fat[j+1] + "        " + fat[j+2]);
		System.out.println();
		showFile();
	}
}

最後使用TestFileSystem這個類實現選單功能
package com.testsystem;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.service.OSManager;

public class TestFileSystem {
	public static void main(String[] args) {
		try{
		OSManager manager = new OSManager();
		meun(manager);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void meun(OSManager manager) {
		Scanner s = new Scanner(System.in);
		String str = null;
		System.out.println("***********" + "歡迎使用檔案模擬作業系統" + "***********");
		System.out.println();
		manager.showFile();

		System.out.println("請輸入命令(輸入help檢視命令表):");
		while ((str = s.nextLine()) != null) {
			if (str.equals("exit")) {
				System.out.println("感謝您的使用!");
				break;
			}

			String[] strs = editStr(str);
			switch (strs[0]) {
			case "createFile":
				if (strs.length < 4) {
					System.out.println("您所輸入的命令有誤,請檢查");
				} else {
					manager.createFile(strs[1], strs[2],
							Integer.parseInt(strs[3]));
				}
				break;
			case "createCatalog":
				if (strs.length < 2) {
					System.out.println("您所輸入的命令有誤,請檢查!");
				} else {
					manager.createCatolog(strs[1]);
				}
				break;
			case "open":
				if (strs.length < 2) {
					System.out.println("您所輸入的命令有誤,請檢查!");
				} else {
					manager.openFile(strs[1]);
				}
				break;
			case "cd":
				if (strs.length < 2) {
					System.out.println("您所輸入的命令有誤,請檢查!");
				} else {
					manager.openFile(strs[1]);
				}
				break;
			case "cd..":
				manager.backFile();
				break;
			case "delete":
				if (strs.length < 2) {
					System.out.println("您所輸入的命令有誤,請檢查!");
				} else {
					manager.deleteFile(strs[1]);
				}
				break;
			case "rename":
				if (strs.length < 3) {
					System.out.println("您所輸入的命令有誤,請檢查!");
				} else {
					manager.reName(strs[1], strs[2]);
				}
				break;
			case "search": {
				if (strs.length < 2) {
					System.out.println("您所輸入的命令有誤,請檢查!");
				} else {
					String[] roadName = strs[1].split("/");
					/*for(String temp : strs)
						System.out.println(temp);
					System.out.println(Arrays.toString(strs));
					System.out.println(Arrays.toString(roadName));*/
					manager.searchFile(roadName);
				}
				break;
			}
			case "showFAT":
				manager.showFAT();
				break;
			case "addContents":
				if (strs.length < 3) {
					System.out.println("您所輸入的命令有誤,請檢查!");
				} else {
					manager.reAdd(strs[1], Integer.parseInt(strs[2]));
				}
				break;
			case "changeType":
				if (strs.length < 3) {
					System.out.println("您所輸入的命令有誤,請檢查!");
				} else {
					manager.changeType(strs[1], strs[2]);
				}
				break;
			case "help": {
				System.out.println("命令如下(空格不能省略):");
				System.out
						.println("createFile FileName fileType fileSize");
				System.out.println("<建立檔案 如:createFile marco txt 5 >");
				System.out.println();
				System.out
						.println("createCatalog FatalogName");
				System.out.println("<建立目錄 如:createCatalog myFile >");
				System.out.println();
				System.out
						.println("open Name.FileTypt");
				System.out.println("<開啟檔案 如:open marco.txt >");
				System.out.println();
				System.out.println("cd CatalogName");
				System.out.println("<開啟目錄 如: cd myFile >");
				System.out.println();
				System.out.println("cd..");
				System.out.println("<返回上級目錄 如: cd..");
				System.out.println();
				System.out
						.println("delete FileName/CatalogName");
				System.out.println("<刪除檔案或目錄(目錄必須為空)如:delete marco >");
				System.out.println();
				System.out
						.println("rename FileName/CatalogName NewName");
				System.out.println("<重新命名檔案或目錄 如: rename myfile mycomputer >");
				System.out.println();
				System.out
						.println("search FileAbsolutedRoad/CatalogAbsolutedRoad");
				System.out.println("<根據絕對路徑尋找檔案或者目錄 如: search root/marco >");
				System.out.println();
				System.out.println("showFAT");
				System.out.println("<檢視FAT表 如: showFAT>");
				System.out.println();
				System.out.println();
				System.out.println("下列命令需要開啟檔案後操作:");
				System.out
						.println("addContents FileName ContentSize");
				System.out.println("<在檔案內增加內容 如:ddContents marco 4 >");
				System.out.println();
				System.out
						.println("changeType FileName newType");
				System.out.println("<改變檔案型別 如: changeType marco doc>");
				System.out.println();
				break;
			}
			default:
				for(String st : strs)
					System.out.println(st);
				System.out.println("您所輸入的命令有誤,請檢查!");
			}
			System.out.println("請輸入命令(輸入help檢視命令表):");
		}
	}

	public static String[] editStr(String str) {
		Pattern pattern = Pattern.compile("([a-zA-Z0-9.\\\\/]*) *");// 根據空格分割輸入命令
		Matcher m = pattern.matcher(str);
		ArrayList<String>  list = new ArrayList<String>();
		while(m.find()){
			list.add(m.group(1));
		}
		String[] strs = list.toArray(new String[list.size()]);
		
		for (int i = 1; i < strs.length; i++) { // 判斷除命令以外每一個引數中是否含有 "."
			int j = strs[i].indexOf(".");
			if (j != -1) { // 若含有"." 將其切割 取前部分作為檔名
				String[] index = strs[i].split("\\."); // 使用轉義字元"\\."
				strs[i] = index[0];
			}
		} 
		return strs;
	}
}