FileSystemDOS
这是一个基于java的简单的仿DOS文件管理系统
使用了java中File的
实现的功能
- 实现
cd
命令 ,支持参数选项 - 实现
mkdir
命令,支持参数选项 - 实现
copy
复制命令,支持参数选项 - 实现
del
删除命令,支持参数选项 - 实现
dir
列出当前路径的子目录和文件,支持参数选项 - 实现
type
查看文件
大致编写的流程
- 确定命令接口Command
- 编写帮助Help
- 写FileController控制流程
- 对着流程一个一个实现命令
- 先编写每个命令的help方法
- 然后在编写excute方法
- 初步测试
- 最后进行简单测试
定义接口Command
/**
* @time 2019年8月6日
* @instruction
* 命令
*/
public interface Command {
/**
* 命令未找到
*/
final int NOTFINDCOMMAND = -1;
/**
* 参数未找到
*/
final int NOTFINDSECONDE = -2;
/**
* 语法异常
*/
final int ERRORCOMMAND = -3;
/**
* 执行成功
*/
final int SUCCESS = 1;
/**
* 未完成
*/
final int UNDONE = 0;
/**
* 不可读
*/
final int DONTREAD = 2;
/**
* 执行出错
*/
final int ERROR = 3;
/**
* 不是目录
*/
final int NOTADIRECTORY = 4;
/**
* 执行命令
*/
int excute(String[] command);
/**
* 命令的帮助
*/
void help();
}
定义了接口状态常量,每个命令都有的执行方法和帮助方法
ps:接口中的命令执行状态常量是逐渐添加的,编写具体命令时需要用到的状态然后再添加
帮助命令Help
/**
* @time 2019年8月9日
* @instruction
* 帮助命令
*/
public class Help implements Command {
public int excute(String[] command) {
//设置状态为未完成
int i = Command.UNDONE;
//用户只输入help,给出帮助概述
if(command.length == 1) {
help();
i = Command.SUCCESS;
}else if(command.length > 2){
//如果参数多于两个,那么命令错误
i = Command.ERRORCOMMAND;
}else {
//传入,执行对应命令
switch(command[1]) {
case "cd":
new Cd().help();
break;
case "dir":
new Dir().help();
break;
case "mkdir":
new Mkdir().help();
break;
case "copy":
new Copy().help();
break;
case "del":
new Del().help();
break;
case "type":
new Type().help();
break;
case "help":
help();
default:
i = Command.NOTFINDSECONDE;
return i;
}
//最后执行成功
i = Command.SUCCESS;
}
return i;
}
/**
* 帮助文档的概述
*/
public void help() {
StringBuilder str = new StringBuilder();
str.append("-------------------------帮助-------------------------\n");
str.append("命令格式:\n");
str.append("命令名称 [参数][参数]\n\n");
str.append("cd \t").append("显示当前目录名或改变当前目录。\n");
str.append("dir \t").append("显示目录中的文件和子目录列表。\n");
str.append("mkdir \t").append("创建目录\n");
str.append("copy \t").append("将一份或多份文件复制到另一个位置。\n");
str.append("del \t").append("删除一个或数个文件。\n");
str.append("type \t").append("显示文本文件的内容\n");
str.append("exit \t").append("退出\n");
str.append("若想查看详细命令,请键入: \"help 命令名称\" 查看 \n");
System.out.println(str);
}
}
先大致的写了帮助概述,然后照着帮助把具体的每一条命令的对应文件建立出来(他们都实现了Command接口),最后写excute方法
控制流程FileController
import java.io.File;
import java.io.IOException;
import java.sql.Date;
import java.util.Map;
import java.util.Scanner;
import com.command.Command;
import com.command.impl.Cd;
import com.command.impl.Copy;
import com.command.impl.Del;
import com.command.impl.Dir;
import com.command.impl.Help;
import com.command.impl.Mkdir;
import com.command.impl.Type;
import com.utils.Init;
public class FileController {
/**
* 当前基本路径
*/
public static String basePath;
/**
* 当前用户
*/
static String user;
/**
* 当前执行的命令
*/
Command CurrentCommand;
Scanner in = new Scanner(System.in);
public FileController() {
//初始化,读配置文件
basePath = Init.path;
user = Init.user;
CurrentCommand = null;
}
public void start() {
System.out.println("欢迎来到 DosfileSystem! 如果需要帮助,请键入 help");
controller();
}
/**
* 格式化命令输入
* @return
*/
public String[] getCinFormat() {
System.out.print(basePath + " #" + user + "#:");
String parse = in.nextLine(); //输入
String[] command = parse.trim().split(" ");
command[0] = command[0].toLowerCase();//全部转换到小写
return command;
}
/**
* 控制中枢
*/
public void controller() {
while(true) {
String[] command = getCinFormat();
switch(command[0]) {
case "cd":
CurrentCommand = new Cd();
break;
case "dir":
CurrentCommand = new Dir();
break;
case "mkdir":
CurrentCommand = new Mkdir();
break;
case "copy":
CurrentCommand = new Copy();
break;
case "del":
CurrentCommand = new Del();
break;
case "type":
CurrentCommand = new Type();
break;
case "exit":
System.exit(0);//退出
case "help":
CurrentCommand = new Help();
break;
default:
System.out.println(command[0] + "未找到");
}
//命令未找到的情况,继续等待用户输入
if(CurrentCommand == null)continue;
//执行命令,返回执行状态
int i = CurrentCommand.excute(command);
showStatus(i,command);
}
}
private void showStatus(int i, String[] command) {
switch(i) {
case Command.SUCCESS :
//执行成功不回显
break;
case Command.ERRORCOMMAND :
System.out.println(command[0] + " 语法错误");
break;
case Command.ERROR:
System.out.println(command[0] + "执行出错");
case Command.NOTFINDCOMMAND :
System.out.println(command[0] + " 未找到该命令");
break;
case Command.NOTFINDSECONDE :
System.out.println(command[1] + " 未找到");
break;
case Command.DONTREAD :
System.out.println(command[1] + " 查看的文件不可读");
break;
case Command.NOTADIRECTORY:
System.out.println(command[3] + " 不是一个目录");
break;
default:
System.out.println("状态码错误");
}
}
}
import com.controller.FileController;
/*
* 入口
**/
public class Main {
public static void main(String[] arg) {
new FileController().start() ;
}
}
程序从入口开始运行,new一个FileController对象,从配置文件中读取配置初始化,调用start方法,开始控制流程controller
controller等待用户输入命令(通过getCinFormat()),得到输入的命令,然后执行(添加一个String数组类型的参数)
初始化的时候,使用工具类Init从配置文件中读取内容,设置basePath和user
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
public class Init {
public static String path;
public static String user;
static {
Properties p = new Properties();
try(
InputStream in = Init.class.getClassLoader().getResourceAsStream("file.properties");
) {
p.load(in);
path = p.getProperty("initPath");
user = p.getProperty("user");
} catch (IOException e) {
e.printStackTrace();
}
}
}
#file.properties
initPath=E:\\fileJavaSystem
user=root
接下来是各个命令的具体编写,每次先写的是help方法定义好规则,然后在写excute方法
cd命令
import java.io.File;
import com.command.Command;
import com.controller.FileController;
public class Cd implements Command {
public int excute(String[] command) {
//初始化命令执行状态,未完成
int i = Command.UNDONE;
//长度为1,说明是"cd",直接显示当前路径
if(command.length == 1) {
System.out.println(FileController.basePath);
i = Command.SUCCESS;
}else if(command.length == 2) {
//"..",返回上层
if( command[1].equals("..") ) {
File file = new File(FileController.basePath);
//如果有上一层,那么切换,否则路径不变
if(file.getParent() != null) {
FileController.basePath = file.getParent();
}
i = Command.SUCCESS;
}else if( command[1].equals(".") ) {
//".",输出当前路径
System.out.println(FileController.basePath);
i = Command.SUCCESS;
}else {
File file = new File(command[1]);
if(!file.isAbsolute()) {
//是相对路径,要拼接
command[1] = FileController.basePath + "\\" + command[1];
}
//如果文件存在,且是一个目录,则切换到目标路径
if(file.exists() && file.isDirectory() ) {
FileController.basePath = file.getAbsolutePath();
i = Command.SUCCESS;
}else {
i = Command.NOTFINDSECONDE;
}
}
}
return i;
}
/**
* cd 帮助
*/
public void help() {
StringBuilder str = new StringBuilder();
str.append("----------------cd help-----------\n");
str.append("命令格式:\n");
str.append("cd [路径] 显示当前目录名或改变当前目录。\n\n");
str.append("只键入 cd 或 cd . 将列出当前目录绝对路径\n");
str.append("键入 cd .. 改变当前目录到上一级\n");
str.append("[路径] 应该表示为:[drive:][path][filename]\n");
str.append("如: cd C:/user 将目录切换到C:/user \n");
System.out.println(str);
}
}
-
cd 命令的长度最多是2,其他的则语法错误
-
“cd …”,“cd .”,“cd”,单独处理
-
“cd 路径”,将路径化为绝对路径,然后切换
copy命令
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import com.command.Command;
import com.controller.FileController;
public class Copy implements Command {
@Override
public int excute(String[] command) {
// 验证命令合法
if (command.length != 3) {
return Command.ERRORCOMMAND;
}
// 目标地址
File target = null;
if (command[2].equals(".")) {// "."代表当前路径
target = new File(FileController.basePath);
} else {
// 绝对或相对路径
if (command[2].contains(":")) {
target = new File(command[2]);
} else {
target = new File(FileController.basePath + "/" + command[2]);
}
}
// 目标路径不是一个目录
if (!target.isDirectory()) {
return Command.ERROR;
}
//复制单个文件
if(!command[1].contains("+")) {
if(command[1].contains(":")) {
//拷贝
copy(new File(command[1]), target);
}else {
//拷贝
copy(new File( FileController.basePath + "/" + command[1]), target);
}
}else {//复制多个文件
// 解析得到要复制的文件列表
File[] files = paraseFile(command[1]);
// 源文件为空
if (files == null) {
return Command.NOTFINDSECONDE;
}
// 依次拷贝
for (File file : files) {
copy(file, target);
}
}
return Command.SUCCESS;
}
/**
* 拷贝文件file到targe目录
* @param file
* @param target
*/
private void copy(File file, File target) {
File file_new = new File(target.getPath() + "/" + file.getName());
try (
InputStream is = new FileInputStream(file);
Reader reader = new InputStreamReader(is);
BufferedReader bufferedReader = new BufferedReader(reader);
OutputStream os = new FileOutputStream(file_new);
Writer writer = new OutputStreamWriter(os);
BufferedWriter bufferedWriter = new BufferedWriter(writer);
) {
// 读出来,然后写进目标
String str;
while ((str = bufferedReader.readLine()) != null) {
bufferedWriter.write(str);
}
} catch (Exception e) {
System.out.println("拷贝" + file.getName() + "失败!");
}
}
/**
* 解析源目标文件
*
* @param string
* @return
*/
private File[] paraseFile(String sources) {
//用"+"分割
String[] strs = sources.split("\\+");
//相应长度的文件数组
File[] files = new File[strs.length];
//遍历,处理相对路径,和验证文件是否存在
for (int i = 0; i < strs.length; i++) {
//处理相对路径
if (!strs[i].contains(":")) {
files[i] = new File(FileController.basePath + "/" + strs[i]);
} else {
files[i] = new File(strs[i]);
}
// 只要有一个文件不存在,那就终止复制
if (!files[i].exists()) {
return null;
}
}
return files;
}
/**
* copy 帮助
*/
public void help() {
StringBuilder str = new StringBuilder();
str.append("----------------copy help-----------\n");
str.append("命令格式:\n");
str.append("coyp 源目标文件 目标地址 复制源文件到目标路径 \n\n");
str.append("数个文件(用 file1+file2+file3 格式)");
str.append("如: copy D:\\fileJavaSystem\\222.txt E:\\fileJavaSystem\\k\\m \n\n");
System.out.println(str);
}
}
- 验证命令参数合法
- 相对路径处理为绝对路径
- 验证各个路径是否存在
- 如果复制多文件要先处理拆分成文件数组paraseFile()
- 最后用copy,拷贝源文件到目标路径
小坑
paraseFile处理多String,通过split("+"),直接分割会报错,因为"+“在正则表达式里有特殊含义,需要做转移处理,需要变成 split(”\+")
del命令
import java.io.File;
import java.util.Scanner;
import com.command.Command;
import com.controller.FileController;
public class Del implements Command {
@Override
public int excute(String[] command) {
//验证命令合法
if(command.length < 2) {
return Command.ERRORCOMMAND;
}
// 是否需要确认,默认不需要
boolean p = false;
// 是否要递归删除,默认否
boolean r = false;
if (command[1].contains("/P") || command[1].contains("/p")) {// 需要确认
p = true;
}
if (command[1].contains("/R") || command[1].contains("/r")) {// 需要递归删除
r = true;
}
//遍历,如果存在,就删除
for (String str : command) {
File file = new File(str);
//如果文件不存在
if (!file.exists()) {
//相对路径,尝试转换为绝对路径
file = new File(FileController.basePath + "//" + str);
if(!file.exists()) {
// 文件不存在,看下一个
continue;
}
}
//文件存在,删除
del(file, p, r);
}
return Command.SUCCESS;
}
/**
* 删除目录或者文件
* @param file
* @param p
* @param r
*/
public void del(File file, boolean p, boolean r) {
//递归的删除
if(file.isDirectory() && file.list().length > 0) {
if(r) {
for(File f : file.listFiles()) {
del(f,p,r);
}
//删除空目录
if(p && !userConfirm(file)) {//需要确认,且用户输入取消,不删除
return;
}else {//否则删除
file.delete();
}
}
}else {
//删除文件
if(p && !userConfirm(file)) {//需要确认,且用户输入取消,不删除
return;
}else {//否则删除
file.delete();
}
}
}
/**
* 向用户询问,是否要继续删除
* @param file
* @return
* 继续删除返回true,否则返回false
*/
public boolean userConfirm(File file) {
Scanner in = new Scanner(System.in);
String who = file.isDirectory()?" <目录> ":" <文件> ";
System.out.println("确认要删除 " + file.getName() + who + " 吗?y继续/其他终止");
String u = in.next();
if(u.equals("y")) {
return true;
}else {
System.out.println(file.getName() + "删除终止");
return false;
}
}
/**
* del 帮助
*/
public void help() {
StringBuilder str = new StringBuilder();
str.append("----------------del help-----------\n");
str.append("命令格式:\n");
str.append("del [参数][names] 删除目录或者文件。\n\n");
str.append(" names 指定一个或多个文件或者目录列表,可以是绝对路径或相对路径\r\n");
str.append("/P 删除每一个文件之前提示确认\n");
str.append("/R 如果删除的是目录,那么进行递归的删除,\n");
str.append("要删除的目录不为空且有子目录,递归删除,否则停止删除\n");
str.append("如: del E:\\fileJavaSystem\\test\\水电费第三方.txt \n\n");
System.out.println(str);
}
}
- 判断参数合法(>=2)
- 根据参数是否需要递归删除目录和删除确认(r,p)
- 处理相对路径为绝对
- 检测文件存在
- 调用删除方法del(file,p,r)
dir命令
import java.io.File;
import java.io.FileFilter;
import java.sql.Date;
import com.command.Command;
import com.controller.FileController;
public class Dir implements Command {
public int excute(String[] command) {
int i = UNDONE;
// 显示目录中的文件和子目录列表
if (command.length == 1) {
i = list(FileController.basePath,"/D/R");
} else if (command.length == 2) {
// 测试command第二个参数是否是一个有效路径
File f = new File(command[1]);
if (f.exists()) {// 存在
//默认,显示子目录、显示文件,不显示隐藏文件
i = list(command[1],"/D/R");
} else {
// 不是,那么可能是附加参数
i = list(FileController.basePath, command[1]);
}
} else if (command.length == 3) {
// 测试command第二个参数是否是一个有效路径
File f = new File(command[1]);
if (f.exists()) {
// 存在,传递附加参数
i = list(command[1], command[2]);
} else {// 不是,语法错误
i = Command.ERRORCOMMAND;
}
} else {// 参数过多,语法错误
i = Command.ERRORCOMMAND;
}
return i;
}
/**
* 根据参数,列出basePath的指定文件
*
* @param basePath 指定路径
* @param parameter 参数
* @return 执行状态
*/
private int list(String basePath, String parameter) {
int i = Command.UNDONE;
if(basePath.charAt(basePath.length()-1) !='\\'){
basePath = basePath + "\\";
}
File file = new File(basePath);
File[] files = null;
try {
//传入一个文件过滤器
files = file.listFiles(new FileFilter() {
// 过滤文件
public boolean accept(File pathname) {
if ( ( parameter.indexOf("/D") != -1 && pathname.isDirectory() ) || //文件
( parameter.indexOf("/R") != -1 && pathname.isFile() ) || //目录
( !pathname.isHidden() && parameter.indexOf("/H") != -1 )
){
return true;
}
return false;
}
});
} catch (Exception e) {
System.out.println("获取目录失败");
}
System.out.println("\n" + file.toString() + "的目录");
System.out.print(showFiles(files));
// 成功
i = Command.SUCCESS;
return i;
}
/**
* 显示文件列表
*
* @param files
* @return
*/
public StringBuilder showFiles(File[] files) {
StringBuilder str = new StringBuilder();
for (File f : files) {
str.append("\n");
str.append(f.getName() + " ").append("<").append(f.isDirectory() ? "目录>" : "文件>" + " ");
str.append(f.length() / 1024.0 / 1024 + "MB ");
str.append("lastModified:").append(new Date(f.lastModified()).toLocalDate()).append("\n");
}
str.append("\n");
return str;
}
/**
* dir 帮助
*/
public void help() {
System.out.println("----------------dir help-----------");
StringBuilder str = new StringBuilder();
str.append("----------------dir help-----------\n");
str.append("命令格式:\n");
str.append("dir [路径] [参数]\n\n");
str.append("只键入 dir 表示列出当前目录中的所有文件和子目录列表");
str.append("[路径] 应该表示为:[drive:][path][filename]\n");
str.append("指定要列出的驱动器、目录和文件\n");
str.append("如: dir C:/user/my.ini \n");
str.append("[参数] 表示显示带有指定属性的文件\n");
str.append("[/D] 显示目录\n");
str.append("[/R] 显示文件\n");
str.append("[/H] 显示隐藏文件\n");
str.append(" 如: dir /D/R/H 列出当前目录下的 目录、制度文件,隐藏文件\n\n");
System.out.println(str);
}
}
写法上和上边的差不多,验证合法性,处理参数,然后调用相应方法
mkdir命令
import java.io.File;
import java.io.IOException;
import javax.xml.stream.events.Comment;
import com.command.Command;
import com.controller.FileController;
public class Mkdir implements Command {
@Override
public int excute(String[] command) {
int i = Command.UNDONE;
//建立目录时,默认不递归
boolean r = false;
if(command.length == 2) {
//递归参数
if(command[1].equals("/R") || command[1].equals("/r")) {//有,第二参数有错误
return Command.NOTFINDSECONDE;
}
//检测路径类型,如果是相对路径,要拼接
if(!command[1].contains(":")) {
command[1] = FileController.basePath + "/" + command[1];
}
i = mkdir(command[1],r);
}else if(command.length == 3){
//递归参数
if(command[1].equals("/R") || command[1].equals("/r")) {//有,递归
r = true;
}else {
return Command.ERRORCOMMAND;
}
//检测路径类型,如果是相对路径,要拼接
if(!command[2].contains(":")) {
command[2] = FileController.basePath + "/" + command[2];
}
i = mkdir(command[2],r);
}else {//命令语法不正确
return Command.ERRORCOMMAND;
}
return i;
}
/**
* 建立目录,当r为true时递归
* @param command
* @param r
* @return
*/
private int mkdir(String command, boolean r) {
File f = new File(command);
File p = f.getParentFile();
//父路径不存在、递归
if(!p.exists() && r) {
//父路径不存在,创建父路径
mkdir(p.getPath(),r);
//然后创建子路径
f.mkdir();
return Command.SUCCESS;
}else if( p.exists()) {
f.mkdir();
return Command.SUCCESS;
}else {
return Command.ERRORCOMMAND;
}
}
/**
* mkdir 帮助
*/
public void help() {
StringBuilder str = new StringBuilder();
str.append("----------------mkdir help-----------\n");
str.append("命令格式:\n");
str.append("mkdir [参数][路径] 新建目录。\n\n");
str.append("/R 如果新建的目录上一级不存在,那么进行递归的建立,\n");
str.append("如: mkdir E:\\fileJavaSystem\\test \n");
str.append("如果要建立的目录test的上级目录fileJavaSsytem不存在,需要加入递归参数 /R ,否则建立失败\n\n");
System.out.println(str);
}
}
与上边基本一致,相比dir有一点不同的,命令的附加参数在excute里边就处理了,在mkdir里传递的是一个boolean类型的,这样写着更简洁;command参数合法的情况有限可以直接列举出来一一处理,其他都为错误
type命令
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import com.command.Command;
import com.controller.FileController;
public class Type implements Command {
@Override
public int excute(String[] command) {
// 判断命令合法
if (command.length != 2) {
return Command.ERRORCOMMAND;
}
File file = new File(command[1]);
if(!file.isAbsolute()) {
file = new File(FileController.basePath + "/" + command[1]);
}
//是一个文件
if (file.isFile()) {
// 是文件且后缀为"txt"
String a = command[1].substring(command[1].length() - 3, command[1].length());
if ("txt".equals(a)) {
return type(file);
}
}
return Command.NOTFINDSECONDE;
}
/**
* 读文件
*/
public int type(File file) {
System.out.println("--------------:" + file.getName());
try(
InputStream is = new FileInputStream(file);
Reader reader = new InputStreamReader(is);
BufferedReader bufferedReader = new BufferedReader(reader);
){
String str;
while( (str = bufferedReader.readLine()) != null ) {
System.out.println(str);
}
return Command.SUCCESS;
}catch(Exception e) {
return Command.ERRORCOMMAND;
}
}
/**
* type 帮助
*/
public void help() {
StringBuilder str = new StringBuilder();
str.append("----------------type help-----------\n");
str.append("命令格式:\n");
str.append("type [文本文件] 查看文本文件\n\n");
str.append("如: type E:\\fileJavaSystem\\test\\水电费第三方.txt \n\n");
System.out.println(str);
}
}
type功能比较简单,就只支持读取txt类型文件
初步完成
最后
- 还有很多命令,没写,待完成
- 通过一个个小demo来学习真是充满了乐趣(自嘲)
- 项目地址
来源:https://blog.csdn.net/the_power/article/details/98957841