基于Java开发的(控制台)模拟的多用户多级目录的文件系统
多级文件系统
1 设计目的
为了加深对文件系统内部功能和实现过程的理解,设计一个模拟的多用户多级目录的文件系统,并实现具体的文件物理结构、目录结构以及较为完善的文件操作命令集。
2 设计内容
2.1系统操作
操作命令风格:本文件系统的操作命令风格将采用windows系统的文件操作命令风格,操作命令区分大小写,并能在用户错误地执行某些操作之后给出及时反馈,提供良好的交互体验。
具体的操作:本文件系统将实现以下功能操作:
2.2文件物理结构
本文件系统的文件物理结构将采用顺序结构,即将文件记录存储在一块连续的磁盘空间中。相应地,磁盘空间的分配也采用连续分配的方式。
2.3文件目录结构
本文件系统的文件目录结构将采用树形目录结构。树形目录结构的优点是可以更方便有效地管理用户的文件,特别是在多用户系统且用户的文件数量较多的情境中。树形目录结构也是目前最为流行的文件目录结构之一,比较符合大多数人的使用习惯。
2.4磁盘空间管理
本文件系统的磁盘空间管理采用位示图来进行管理,用户可以使用show命令来显示磁盘空间位示图以查看当前系统的磁盘空间使用情况。
2.5额外功能
本文件系统提供查看系统操作命令集的功能,用户可以使用help命令来查看本系统已实现的功能,优化用户的使用体验。
另外,本文件系统还提供了将创建的文件保存到电脑本地的功能,以便用户下次使用本系统时能查看自己所创建的文件。2.6编程语言和环境
本文件系统的编程语言采用Java语言,运行环境为JDK 1.8版本及以上。
3 设计步骤
3.1需求分析
3.1.1功能需求
本文件系统的功能模块将细分为三个模块:用户管理模块、文件管理模块、系统管理模块。各个模块的功能需求如表3.1~表3.3所示。
**表 3. SEQ 表 _ ARABIC \s 1 1 用户管理模块功能需求_
名称 | 详细描述 |
---|---|
用户注册 | 用户使用register命令注册一个系统用户,并要求输入用户名和密码 |
用户登录 | 用户使用login命令进行登录,需要输入用户名和密码进行身份校验 |
用户注销 | 用户使用logout命令注销当前已登录的用户 |
表 3.2 文件管理模块功能需求
名称 | 详细描述 |
---|---|
创建目录 | 用户使用mkdir命令创建一个文件目录,文件目录名不能和当前目录下的文件或文件目录重名,并且不区分大小写 |
列出文件 | 用户使用dir命令列出当前目录下的所有文件的详细信息,支持使用路径名 |
切换目录 | 用户使用cd命令切换到目标目录,路径名使用windows系统的风格 |
创建文件 | 用户使用create命令创建一个文件,文件名不能和当前目录下的文件或文件目录重名,并且不区分大小写 |
打开文件 | 用户使用open命令打开一个文件,支持使用路径名 |
读取文件 | 用户使用read命令读取一个已打开的文件,可向后读取和向前读取 |
写入文件 | 用户使用write命令向一个已打开的文件写入记录 |
关闭文件 | 用户使用close命令关闭一个已打开的文件 |
删除文件 | 用户使用delete命令删除一个存在的文件或文件目录,支持使用路径名 |
重命名文件 | 用户使用rename命令对一个文件或文件目录进行重命名,新的名称不能和当前目录下的文件或文件目录重名,并且不区分大小写 |
表 3.3 系统管理模块功能需求
名称 | 详细描述 |
---|---|
显示帮助 | 用户使用help命令查看当前系统支持的操作命令以及使用方法 |
显示磁盘位示图 | 用户使用show命令查看当前磁盘空间的位示图和其他信息 |
退出系统 | 用户使用exit命令退出文件系统,系统在退出前会保存用户信息、文件信息和磁盘空间信息 |
3.1.2性能需求
本文件系统具有较高的响应比,在文件信息不是特别多的时候能够做到0延迟响应。并且本文件系统运行所需的内存空间比较小,保存文件的大小与文件信息的多少成正比。
3.1.3交互需求
用户与本文件系统的交互在控制台中进行,并且可以方便地查看系统操作命令,降低了用户的学习使用成本。
本文件系统具有良好的信息展示功能,包括文件信息展示、文件内容展示、操作命令展示、位示图展示,使用户可以及时地获取自己所需要的信息。
本文件系统具有友好的错误信息反馈。在用户想要执行一项不被允许的操作时(如打开不存在的文件)能够给予用户正确、简洁的引导式的错误反馈。
3.2概要设计
3.2.1设计思想
本文件系统在设计上采用模块化的思想,将服务类型一致的功能划分到同一个模块,使项目整体上比较清晰。并且每个模块都提供对外调用的接口,这样就能让其他模块在需要的时候调用本模块的某些功能,实现复用。
3.2.2模块设计
本文件系统分为用户管理模块、文件管理模块和系统管理模块三个大模块。其中各个模块细分如下:
- 用户管理模块:负责提供用户注册、用户登录、用户注销三个功能。
- 文件管理模块:文件管理模块还可分为文件模块、文件目录模块以及磁盘模块三个小模块。其中:
- 文件模块:负责提供创建文件、打开文件、读取文件、写入文件、关闭文件、删除文件以及重命名文件共七个功能。
- 文件目录模块:负责提供创建目录、切换目录、列出文件以及解析路径共四个功能。
- 磁盘模块:负责提供存储记录、释放磁盘空间、读取记录、保存磁盘数据以及加载磁盘数据共五个功能。
- 系统管理模块:系统管理模块有一个下属模块:打印信息模块,该模块负责提供显示文件列表、显示文件内容、显示帮助列表以及显示位示图共四个功能。
具体的系统模块图如图3.1所示。
图3.1 系统模块图
3.2.3抽象数据类型定义
系统用户信息实体类定义如下:
public class User implements Serializable {/*** 用户名*/private String username;/*** 密码*/private String password;
}
文件控制块实体类定义如下:
public class FileControlBlock implements Serializable {/*** 是否是目录文件*/private boolean isDirectory;/*** 文件名(包括了拓展名)*/private String fileName;/*** 拓展名*/private String suffix;/*** 起始盘块号*/private Integer startBlock;/*** 所占用的盘块数* 文件大小 = 一个盘块的大小 * 所占用的盘块数*/private Integer blockNum;/*** 文件属性:保护码列表*/private List<ProtectType> protectTypeList;/*** 创建时间*/private LocalDateTime createTime;/*** 最后一次修改时间*/private LocalDateTime updateTime;
}
树形目录结构实体类定义如下:
public class Directory implements Serializable {/*** 文件控制块*/private FileControlBlock fileControlBlock;/*** 在树形目录结构中的位置*/private Integer index;/*** 文件夹属性:子目录项集合*/private List<Directory> childDirectory;/*** 父目录项的位置*/private Integer parentIndex;
打开的文件信息实体类定义如下:
public class ActiveFile {/*** 文件控制块*/private FileControlBlock fileControlBlock;/*** 文件记录*/private List<Character> fileRecord;/*** 读指针*/private Integer readPtr;/*** 写指针*/private Integer writePtr;
}
虚拟磁盘空间实体类定义如下:
public class Disk implements Serializable {/*** List<Character>: 表示一个盘块* List<List<Character>>: 表示所有盘块的集合,即一个磁盘*/private List<List<Character>> disk;/*** 表示存储在该磁盘上的系统用户集*/private Map<String, User> userMap;/*** 表示存储在该磁盘上的所有文件控制块*/private List<FileControlBlock> fileControlBlockList;/*** 表示存储在该磁盘上的树形结构目录,第0个元素为根目录*/private List<Directory> directoryStruct;/*** 表示存储在该磁盘上的磁盘位示图*/private Integer[][] bitmap;
}
3.2.4主程序的流程图
主程序的流程图如图3.2所示。
图3.2 主程序流程图
3.3详细设计
3.3.1系统算法IPO表
表3.4~表3.26展示了本文件系统的各个功能的具体实现算法IPO表,介绍了各功能模块的编号、模块、日期、作者、被调用模块、输入输出、数据处理与相关数据。如下所示:
表3.4用户注册IPO表
表3.5用户登录IPO表
表3.6用户注销IPO表
表3.7创建文件IPO表
表3.8打开文件IPO表
表3.9读取文件IPO表
表3.10写入文件IPO表
表3.11关闭文件IPO表
表3.12删除文件IPO表
表3.13重命名文件IPO表
表3.14创建目录IPO表
表3.15切换目录IPO表
表3.16列出文件IPO表
表3.17解析路径IPO表
表3.18存储记录IPO表
表3.19释放磁盘空间IPO表
表3.20读取记录IPO表
表3.21保存磁盘数据IPO表
表3.22加载磁盘数据IPO表
表3.23显示文件列表IPO表
表3.24显示文件内容IPO表
表3.25显示帮助列表IPO表
表3.26显示位示图IPO表
3.4调试分析
一开始在实现树形结构目录的时候,我的文件目录项实体类是这样的:
public class Directory implements Serializable {/*** 文件控制块*/private FileControlBlock fileControlBlock;/*** 文件夹属性:子目录项集合*/private List<Directory> childDirectory;/*** 父目录项*/private Directory parentDirectory;
}
在一个目录项之中,持有其父目录项的引用,如果该目录项是目录文件的话,还持有归属于它的子目录项的集合,这样便可以实现一个树形结构的目录。而在磁盘中只要持有一个对根目录的引用就可以查找到所有的目录项,这样是很方便的一种实现方案。
但是,这样的实体类在程序实际运行起来的时候是有问题的。假设有这样的情形:directoryA是一个表示目录文件的目录项,directoryB是一个表示数据文件的目录项,并且directoryB的父目录项是directoryA。这样的关系,在程序中表现为,directoryA的childDirectory属性中持有对directoryB的引用,而directoryB的parentDirectory属性持有对directoryA的引用,这样就造成了循环引用。
这样的循环引用问题,在Java的序列化和反序列化的时候表现的尤为明显。因为本文件系统可以将系统运行过程中的磁盘数据保存到本地文件中,也可以将本地文件中的磁盘数据加载到程序中,前者是通过序列化实现的,后者是通过反序列化实现的。但是由于循环引用的问题,在进行序列化和反序列化的时候,会报栈溢出的错误,导致磁盘数据加载不了(表现为找不到本该存在的目录项)。
当时发现这个问题的时候,我通过IDEA的debug功能一步一步地调试,最终定位到问题的根源以及想出了解决方案。改进后的文件目录项实体类如下所示:
public class Directory implements Serializable {/*** 文件控制块*/private FileControlBlock fileControlBlock;/*** 在树形目录结构中的位置*/private Integer index;/*** 文件夹属性:子目录项集合*/private List<Directory> childDirectory;/*** 父目录项的位置*/private Integer parentIndex;
}
这样进行了修改之后,在磁盘中要保存的是整个树形目录结构的所有目录项的列表,如下所示:
public class Disk implements Serializable {.../*** 表示存储在该磁盘上的树形结构目录,第0个元素为根目录*/private List<Directory> directoryStruct;...
}
3.5系统测试
3.5.1 help命令测试
测试数据如下表:
前提条件 | 命令输入 | 执行结果 | 结果分析 |
---|---|---|---|
无 | help | 显示本系统的帮助列表 | help命令功能正常 |
实际测试结果如下图所示:
3.5.2 exit命令测试
测试数据如下表:
前提条件 | 命令输入 | 执行结果 | 结果分析 |
---|---|---|---|
无 | exit | 退出本系统 | exit命令功能正常 |
实际测试结果如下图所示:
3.5.3 register命令测试
测试数据如下表:
前提条件 | 命令输入 | 执行结果 | 结果分析 |
---|---|---|---|
无 | register | 注册用户失败:用户名不能为空 | 缺少[用户名]参数 |
无 | register pw | 注册用户失败:密码不能为空 | 缺少[密码]参数 |
[pw]这个用户没有被注册 | register pw 123 | 注册用户成功 | register命令功能正常 |
[pw]这个用户已被注册 | register pw 123 | 注册用户失败:该用户已存在 | [pw]是一个已存在的用户,不能重复注册 |
实际测试结果如下图所示:
3.5.4 login命令测试
测试数据如下表:
前提条件 | 命令输入 | 执行结果 | 结果分析 |
---|---|---|---|
无 | login | 用户登录失败:该用户不存在 | 缺少[用户名]参数,系统会判定[用户名]为空字符 |
无 | login pw | 用户登录失败:密码错误 | 缺少 [密码]参数,系统会判定[密码]为空字符 |
[pw]这个用户已被注册,且密码不是456 | login pw 456 | 用户登录失败:密码错误 | [pw]这个用户的密码不正确 |
[unknown]这个用户没有被注册 | login unknown 123 | 用户登录失败:该用户不存在 | [unknown]这个用户不存在(没有注册过) |
[pw]这个用户已被注册,且密码是123 | login pw 123 | 登录成功 | 用户名和密码都正确,登录成功 |
实际测试结果如下图所示:
3.5.5 logout命令测试
测试数据如下表:
前提条件 | 命令输入 | 执行结果 | 结果分析 |
---|---|---|---|
已登录了某个用户 | logout | 注销成功 | logout命令功能正常 |
当前没有登录用户 | logout | 用户注销失败:当前没有登录用户 | logout命令功能正常 |
实际测试结果如下图所示:
3.5.6 show命令测试
测试数据如下表:
前提条件 | 命令输入 | 执行结果 | 结果分析 |
---|---|---|---|
无 | show | 显示位示图信息 | show命令功能正常 |
实际测试结果如下图所示:
注意:以下命令能够正确执行前提都是要在已进行了登录的情况下,如果没有进行登录,系统会给出错误提示信息。所以下述命令的测试都是在已登录了的情况下进行的测试,此前提条件不再赘述,忘悉知。
3.5.7 mkdir命令测试
测试数据如下表:
前提条件 | 命令输入 | 执行结果 | 结果分析 |
---|---|---|---|
无 | mkdir | 创建目录失败:文件名不能为空 | 缺少[目录名]这个参数,系统会判定目录名为空字符 |
当前目录下不存在[os]这个子目录 | mkdir os | 创建成功,使用[dir]命令可以查看到[os]这个目录的信息 | mkdir命令功能正常 |
当前目录下存在[os]这个子目录 | mkdir os | 创建目录失败:文件名重复 | 同一个目录中的文件不能重名 |
实际测试结果如下图所示:
3.5.8 dir命令测试
测试数据如下表:
前提条件 | 命令输入 | 执行结果 | 结果分析 |
---|---|---|---|
无 | dir | 列出当前目录下面所有文件的信息 | dir命令功能正常 |
实际测试结果如下图所示:
3.5.9 cd命令测试
测试数据如下表:
前提条件 | 命令输入 | 执行结果 | 结果分析 |
---|---|---|---|
无 | cd | 仍在当前目录 | 如果缺少[目录路径]参数,则停留在当前目录下 |
当前目录下存在[os]这个子目录 | cd os | 进入到os目录 | cd命令功能正常 |
当前目录下不存在[os]这个子目录 | cd os | 切换目录失败:找不到对应目录 | cd命令功能正常 |
存在上一级目录 | cd … | 返回到上一级目录 | cd命令功能正常 |
实际测试结果如下图所示:
3.5.10 create命令测试
测试数据如下表:
前提条件 | 命令输入 | 执行结果 | 结果分析 |
---|---|---|---|
无 | create | 创建文件失败:文件名不能为空 | 缺少[文件名]这个参数,系统会判定文件名为空字符 |
当前目录下不存在[a.txt]这个文件 | create a.txt | 创建文件成功,使用[dir]命令可以查看到[a.txt]的信息 | create命令功能正常 |
当前目录下存在[a.txt]这个文件 | create a.txt | 创建文件失败:文件名重复 | 同一个目录中的文件不能重名 |
当前目录下存在[a.txt]这个文件 | create A.txt | 创建文件失败:文件名重复 | 本系统的文件名不区分大小写 |
实际测试结果如下图所示:
3.5.11 open命令测试
测试数据如下表:
前提条件 | 命令输入 | 执行结果 | 结果分析 |
---|---|---|---|
无 | open | 打开文件失败:文件名不能为空 | 缺少[文件名]这个参数,系统会判定文件名为空字符 |
当前目录下不存在[b.jpg]这个文件 | open b.jpg | 打开文件失败:文件不存在 | open命令功能正常 |
当前目录下存在[a.txt]这个文件 | open a.txt | 打开文件成功,显示出文件中的内容 | open命令功能正常 |
实际测试结果如下图所示:
3.5.12 close命令测试
测试数据如下表:
前提条件 | 命令输入 | 执行结果 | 结果分析 |
---|---|---|---|
当前存在已打开的文件 | close | 关闭成功 | close命令功能正常 |
当前不存在已打开的文件 | close | 关闭文件失败:当前没有文件被打开 | close命令功能正常 |
实际测试结果如下图所示:
3.5.13 write命令测试
测试数据如下表:
前提条件 | 命令输入 | 执行结果 | 结果分析 |
---|---|---|---|
当前不存在已打开的文件 | write,然后输入文件内容,以“###”结尾 | 写入文件失败:请先打开文件再进行写入 | write命令功能正常 |
当前存在已打开的文件 | write,然后输入文件内容,以“###”结尾 | 写入成功,控制台会回显文件的所有内容(包括刚刚写入的内容) | write命令功能正常 |
实际测试结果如下图所示:
3.5.14 read命令测试
测试数据如下表:
前提条件 | 命令输入 | 执行结果 | 结果分析 |
---|---|---|---|
当前存在已打开的文件 | read | 显示空字符 | 缺少[要读取的记录个数]参数,系统会判定要读取的记录个数为0 |
当前存在已打开的文件,且(当前读指针 + 5)< 文件长度 | read 5 | 显示读取出来的5个字符 | read命令功能正常 |
当前存在已打开的文件,且(当前读指针 - 4)>= 0 | read -4 | 显示读取出来的4个字符 | read命令支持向后读取 |
实际测试结果如下图所示:
3.5.15 delete命令测试
测试数据如下表:
前提条件 | 命令输入 | 执行结果 | 结果分析 |
---|---|---|---|
无 | delete | 删除文件失败:文件名不能为空 | 缺少[文件名]参数,系统会判定文件名为空字符 |
当前目录下不存在[b.jpg]这个文件 | delete b.jpg | 删除文件失败:找不到对应文件 | delete命令功能正常 |
当前目录下存在[a.txt]这个文件 | delete a.txt | 删除成功,使用[dir]命令查看a.txt文件不存在 | delete命令功能正常 |
实际测试结果如下图所示:
3.5.16 rename命令测试
测试数据如下表:
前提条件 | 命令输入 | 执行结果 | 结果分析 |
---|---|---|---|
无 | rename | 重命名文件失败:文件名不能为空 | 缺少[文件名]参数,系统会判定文件名为空字符 |
无 | rename b.jpg | 重命名文件失败:文件名不能为空 | 缺少[新文件名]参数,系统会判定新文件名为空字符 |
当前目录下不存在[b.jpg]这个文件 | rename b.jpg c.mp3 | 重命名文件失败:文件不存在 | rename命令功能正常 |
当前目录下存在[a.txt]这个文件 | rename a.txt c.mp3 | 重命名成功,使用[dir]命令可查看a.txt已变为c.mp3 | rename功能正常 |
实际测试结果如下图所示:
3.6使用说明
3.6.1操作命令说明
本文件系统支持的所有操作命令已在“帮助列表”中列出。想要查看“帮助列表”时只要键入 help 命令即可,如下所示:
现在对本文件系统中的几种操作命令格式做说明:
①无参数命令:例如help、show、exit、dir等都是无参数命令。在使用无参数命令时,只需要键入对应的命令即可,不需要输入额外的参数。如果输入的额外的参数,系统会默认把它丢弃掉,也并不影响使用。
②单参数命令:例如mkdir、create、open等都是单参数命令。使用单参数命令时,输入对应的命令之后,要输入一个(或者至少一个)空格,然后输入正确的参数,最后回车即可。在上述“帮助列表”中的“用法”中,方括号框中表示的是要输入的参数。例如mkdir [目录名],该命令正确的用法是“mkdir test”,表示创建一个名为“test”的新目录。
③多参数命令:例如register、login等都是要输入一个以上参数的命令。在使用多参数命令时,参数和参数之间要使用一个空格进行分隔开。另外,参数的个数要对应。例如在使用login [用户名] [密码],如果输入“login 123”,系统会把“123”当成用户名来识别,而该命令缺少了密码这个参数,系统给出错误提示。
另外,对本文件系统采用的文件路径格式做如下说明:
①目录名与目录名(或文件名)之前用路径分隔符“/”分隔开。例如“cd test/word”表示进入到当前目录下的test文件夹下的word文件夹中。
②全部采用相对路径
③“…”表示上一级目录。例如“cd …”表示回退到上一级目录。
3.6.2磁盘数据保存说明
本文件系统在启动的时候会加载“固定路径”下的“磁盘文件”中的磁盘数据,在正常退出的时候会保存磁盘数据到“固定路径”下的“磁盘文件”中。
“固定路径”是指程序运行文件所在目录中的save文件夹。“磁盘文件”是指save文件夹中的一个名为disk.ser的文件,本文件系统的所有磁盘数据都会保存进里面。如果本文件系统启动的时候找不到disk.ser文件(或save文件夹),系统会初始化磁盘,相当于一个新激活的系统。
本文件系统在正常退出(使用exit命令退出)的之前,会保存当前的磁盘数据到save文件夹中的disk.ser文件中。如果disk.ser文件不存在,会新建一个disk.ser文件;如果disk.ser文件存在,则会进行覆盖写入操作。
3 经验与体会
我的经验和体会是:
①在编程实现多级文件系统之前,要先确定好文件的逻辑结构、文件的物理结构、文件控制块信息、树形目录结构的表现形式、磁盘空间的表示显示、文件记录在磁盘中的表现形式、磁盘空间的分配方式、磁盘空间的管理形式等重要问题。只有思考了以上问题以及确定好具体的实现方案之后,才能着手与正式的编程实现,否则就在编程的同时遇到很多具有极强关联性的问题,导致了编程后期可能要对系统进行重构。
②在设计本文件系统的交互的时候,要考虑到用户的使用感受,应该使用户能以尽量简单的形式使用本系统,操作命令的输入不要过于冗余而繁杂。在这个方面可以参考很多优秀的操作系统的实现方式,比如Linux和Windows系统。
4 重要数据结构或疑难部分说明
4.1重要数据结构
大部分重要数据结构已经在之前的设计步骤中的需求分析的抽象数据类型定义小节中进行了说明,这里便不再赘述。关于树形结构目录的实现则在实体类Directory中。
4.2疑难部分说明
除了树形目录结构的具体实现比较难之外(该实现已在调试分析中进行说明),另一个疑难部分就是在存储文件记录时为文件记录分配磁盘空间和修改位示图的具体实现。存储文件记录的具体实现为:
public CommonResult<FileControlBlock> storeRecord(FileControlBlock fileControlBlock, List<Character> record) {if (CollectionUtil.isEmpty(record)) {return CommonResult.operateSuccess(null);}// 修改位示图,为重新分配盘块做准备changeBitmapStatus(fileControlBlock.getStartBlock(), fileControlBlock.getBlockNum(), true);// 计算存储该长度记录所需要的盘块数量Integer requiredNum = Math.ceilDivide(record.size(), DiskConstant.BLOCK_SIZE);int count = 0;int startBlockId = DiskConstant.RECORD_START_BLOCK;for (int i = 0; i < DiskConstant.BITMAP_ROW_LENGTH; i++) {for (int j = 0; j < DiskConstant.BITMAP_LINE_LENGTH; j++) {// 如果该盘块空闲if (DiskConstant.BITMAP_FREE.compareTo(Memory.getInstance().getBitmap()[i][j]) == 0) {if (count == 0) {// 记下起始盘块号: 当前行 * 总列数 + 当前列startBlockId = i * DiskConstant.BITMAP_LINE_LENGTH + j;}count++;if (count == requiredNum) {// 如果有足够的连续盘区供存储,则进行存储,并改变位示图的相应状态storeToDisk(startBlockId, record);changeBitmapStatus(startBlockId, requiredNum, false);return CommonResult.operateSuccess(fileControlBlock.setStartBlock(startBlockId).setBlockNum(requiredNum));}} else {// 因为是连续分配,如果该盘块不空闲则要重新计数count = 0;}}}return CommonResult.operateFailWithMessage("[分配盘块失败]: 磁盘空间不足");
}
在该方法中调用了两个关键的方法:storeToDisk(startBlockId, record)和changeBitmapStatus(startBlockId, requiredNum, false)。
其中的storeToDisk(startBlockId, record)方法的作用是将文件记录存储在磁盘中。传入参数startBlockId表示起始盘块号,传入参数record表示待存储的文件记录。其具体实现为:
private void storeToDisk(Integer startBlockId, List<Character> record) {int index = 0;int blockId = startBlockId;Disk disk = DataCache.getInstance().getDisk();// 可以直接覆盖掉原来的磁盘中的记录for (Character ch : record) {if (index >= DiskConstant.BLOCK_SIZE) {// 如果一个盘块的空间被用完了,则使用下一个盘块来进行存储blockId++;index = 0;}disk.getDisk().get(blockId).add(index, ch);index++;}while (index < DiskConstant.BLOCK_SIZE && disk.getDisk().get(blockId).size() > index) {// 擦除最后一个盘块中没有用到的空间disk.getDisk().get(blockId).set(index, null);index++;}
}
其中的changeBitmapStatus(startBlockId, requiredNum, false)方法的作用是更改相应的位示图的状态(仅适用于连续分配的方式)。传入参数startBlockId表示起始盘块号,传入参数blockNum表示要更改的盘块数量,传入参数changeToFree表示要更改的状态(true-表示空闲状态,false-表示已分配状态)。其具体实现为:
private void changeBitmapStatus(Integer startBlockId, Integer blockNum, boolean changeToFree) {if (Objects.isNull(startBlockId) || startBlockId < DiskConstant.RECORD_START_BLOCK ||startBlockId >= DiskConstant.BLOCK_NUM || blockNum <= 0) {return;}// 解析该盘块在位示图中的第几行int row = startBlockId / DiskConstant.BITMAP_LINE_LENGTH;// 解析该盘块在位示图中的第几列int line = startBlockId % DiskConstant.BITMAP_LINE_LENGTH;for (int i = 0; i < blockNum; i++) {Memory.getInstance().getBitmap()[row][line] = changeToFree ? DiskConstant.BITMAP_FREE : DiskConstant.BITMAP_BUSY;if (line >= DiskConstant.BITMAP_LINE_LENGTH - 1) {line = 0;row++;} else {line++;}}
}
相关文章:

基于Java开发的(控制台)模拟的多用户多级目录的文件系统
多级文件系统 1 设计目的 为了加深对文件系统内部功能和实现过程的理解,设计一个模拟的多用户多级目录的文件系统,并实现具体的文件物理结构、目录结构以及较为完善的文件操作命令集。 2 设计内容 2.1系统操作 操作命令风格:本文件系统的…...

tailwindcss group-hover 不生效
无效 <li class"group"><div class"tw-opacity-0 group-hover:tw-opacity-100" /> </li>配了tw前缀,group要改成tw-group // tailwind.config.jsmodule.exports {prefix: "tw-", }<li class"tw-group&q…...

python环境配置问题(个人经验)
很久没配置 python 新环境了,最近新项目需要进行配置,在配置过程中发现了不少问题,记录下。 问题1:fatal error: longintrepr.h: 没有那个文件或目录 这个问题的原因是新环境的 python 版本(3.10以上)与本地的版本(3.8.x)差异过…...

BERT训练之数据集处理(代码实现)
目录 1读取文件数据 2.生成下一句预测任务的数据 3.预测下一个句子 4.生成遮蔽语言模型任务的数据 5.从词元中得到遮掩的数据 6.将文本转化为预训练数据集 7.封装函数类 8.调用 import os import random import torch import dltools 1读取文件数据 def _read_wiki(data_d…...

一款辅助渗透测试过程,让渗透测试报告一键生成
《网安面试指南》http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247484339&idx1&sn356300f169de74e7a778b04bfbbbd0ab&chksmc0e47aeff793f3f9a5f7abcfa57695e8944e52bca2de2c7a3eb1aecb3c1e6b9cb6abe509d51f&scene21#wechat_redirect 《Java代码审…...

力扣最热一百题——颜色分类
目录 题目链接:75. 颜色分类 - 力扣(LeetCode) 题目描述 示例 提示: 解法一:不要脸用sort Java写法: 运行时间 解法二:O1指针 Java写法: 重点 运行时间 C写法:…...

2024年工业制造企业CRM研究报告:需求清单、市场格局、案例分析
我国是世界上产业体系最完备的国家,拥有全球规模最大、门类最齐全的生产制造体系,在500种主要工业产品中,有四成以上产品产量位居全球第一。2023年制造业增加值达33万亿元,占世界的比重稳定在30%左右,我国制造业增加值…...

Spring MVC参数接收 总结
1. 简介 Spring MVC可以简化从前端接收参数的步骤。 2. Param传参 通过设定函数入参和添加标记来简化接受: //参数接收 RequestMapping("product") ResponseBody //接受/product?productgoods&id123 //1.名称必须相同,2.不传值不会不…...

Docekrfile和docker compose编写指南及注意事项
Dockerfile 基础语法 我们通过编写dockerfile,将每一层要做的事情使用语法固定下来,之后运行指令就可以通过docker来制作自己的镜像了。 构建镜像的指令:docker build /path -t imageName:tag 注意,docker build后的path必须是dockerfile…...

VITS源码解读6-训练推理
1. train.py 1.1 大体流程 执行main函数,调用多线程和run函数执行run函数,加载日志、数据集、模型、模型优化器for循环迭代数据batch,每次执行train_and_evaluate函数,训练模型 这里需要注意,源码中加载数据集用的分…...

力扣 简单 104.二叉树的最大深度
文章目录 题目介绍解法 题目介绍 解法 如果知道了左子树和右子树的最大深度 l 和 r,那么该二叉树的最大深度即为max(l,r)1,而左子树和右子树的最大深度又可以以同样的方式进行计算。因此我们可以用递归的方法来计算二叉树的最大深度。具体而言ÿ…...

单片机长短按简单实现
单片机长短按简单实现 目录 单片机长短按简单实现1 原理2 示例代码2.1 按键实现 3 测试log4 其他实现方式 1 原理 按键检测和处理的步骤如下: 1:定时扫描按键(使用定时器定时扫描,也可以用软件延时或者系统心跳之类的方式&#…...

如何用好通义灵码企业知识库问答能力?
通义灵码企业版:通义灵码企业标准版快速入门_智能编码助手_AI编程_智能编码助手通义灵码(Lingma)-阿里云帮助中心 通义灵码提供了基于企业知识库的问答检索增强的能力,在开发者使用通义灵码 IDE 插件时,可以结合企业知识库内上传的文档、文件…...

C语言自定义类型:联合体
目录 前言一、联合体1.1 联合体类型的声明1.2 联合体的特点1.3 相同成员的结构体和联合体对比1.4 联合体大小的计算1.5 联合体的⼀个练习 总结 前言 前面我讲到C语言中的自定义结构——结构体,其实C语言中的自定义结构不只有结构体,还有枚举和联合体&am…...

【JavaEE】——线程池大总结
阿华代码,不是逆风,就是我疯, 你们的点赞收藏是我前进最大的动力!!希望本文内容能够帮助到你! 目录 引入:问题引入 一:解决方案 1:方案一——协程/纤程 (1…...

编程中为什么使用0和1表示状态
前言 这是我在这个网站整理的笔记,有错误的地方请指出,关注我,接下来还会持续更新。 作者:神的孩子都在歌唱 我们看到很多项目都使用0和1表示某些状态信息,具体含义取决于上下文。以下是一些常见的用法: 布尔值&#x…...

C++入门基础知识90(实例)——实例15【求两数的最大公约数】
成长路上不孤单😊😊😊😊😊😊 【14后😊///C爱好者😊///持续分享所学😊///如有需要欢迎收藏转发///😊】 今日分享关于求两数的最大公约数的相关内容ÿ…...

自动化办公-Python-os模块的使用
os.path 模块的使用 在指定文件路径时,由于操作系统的差异,直接使用硬编码的路径可能会导致程序在不同平台上无法正常运行。为了解决这个问题,Python 提供了 os.path 模块,它包含了一系列用于路径操作的函数,可以帮助您…...

无人机之数据处理技术篇
一、数据采集 无人机通过搭载的各种传感器和设备,如GPS、加速度计、陀螺仪、磁力计、激光雷达(LiDAR)、高光谱相机(Hyperspectral)、多光谱相机(Multispectral)以及普通相机等,实时采集飞行过程中的各种数据。这些数据包括无人机的位置、速度、高度、姿态…...

828华为云征文|部署多功能集成的协作知识库 AFFiNE
828华为云征文|部署多功能集成的协作知识库 AFFiNE 一、Flexus云服务器X实例介绍二、Flexus云服务器X实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置2.4 Docker 环境搭建 三、Flexus云服务器X实例部署 AFFiNE3.1 AFFiNE 介绍3.2 AFFiNE 部署3.3 AFFiNE 使用 四、…...

c++(AVL树及其实现)
一、AVL树的概念 AVL树是最先发明的自平衡⼆叉查找树,AVL是⼀颗空树,或者具备下列性质的⼆叉搜索树:它的 左右子树都是AV树,且左右子树的高度差的绝对值不超过1。AVL树是⼀颗高度平衡搜索⼆叉树, 通过控制高度差去控…...

Cesium GIS项目关于湖泊识别与提取的实现
1. 引言 项目背景 随着遥感技术的发展,地理信息系统的应用越来越广泛。本项目旨在开发一个基于Cesium的地理信息系统,利用深度学习技术自动识别并显示湖泊的位置。 目标与意义 通过自动化处理大量遥感影像数据,提高湖泊监测的效率和准确性,为水资源管理和环境保护提供支…...

两个圆形 一个z里面一个z外面,z里面的大,颜色不同 html
两个圆形 一个z里面一个z外面,z里面的大,颜色不同 html <!DOCTYPE html> <html> <head> <style> .outer-circle {width: 150px;height: 150px;border-radius: 50%;background-color: #ff9999; /* 外圆的颜色 */position: relat…...

【Power Query】M函数-table
M函数-table 添加列(AddColumn):条件语句(If..then..else):容错语句(try..otherwise): 排序(ReorderColumns):筛选(Selec…...

uni-app 封装websocket 心跳检测,开箱即用
class websocketUtils {constructor(url, needbeat, options {}) {this.needbeat needbeat;this.url url;this.options options;this.ws null;this.heartbeatInterval options.heartbeatInterval || 10000; // 心跳间隔,默认为10秒 this.reconnectInterval …...

ASP.NET Core8.0学习笔记(十九)——EF Core DbSet
一、DbSet概述 1.DbSet提供了通过DbContext对表进行查询操作的路径。DbSet对应的属性名称将默认映射为实体T的表名。 2.使用DbSet<T>进行查询的方法: (1)直接在DbContext中创建对应的DbSet<T>属性 (2)使用DbSet DbContext.Set<T>方法操作数据表。…...

Android Camera 预览角度和拍照保存图片角度相关
–基于Android R(11) 关于Camera Camera Framework 的架构 Android Camera Framework 是一个分层架构,由以下组件组成: HAL(硬件抽象层): HAL 抽象底层相机硬件,提供与不同设备相机进行交互的标准接口.CameraService : Camera…...

新手如何使用Qt——方法使用
前言 那么这篇文章其实是我在使用Qt的过程当中呢,我发现在Qt使用过程中,在我理解信号和槽这个概念后,在编写槽函数数的时候,发现了自身存在的问题,我的难点是在于当我在编写槽函数的时候,我知道这个槽函数是…...

友元运算符重载函数
目录 1.定义友元运算符重载函数的语法形式 2.双目运算符重载 3.单目运算符重载 1.定义友元运算符重载函数的语法形式 (1)在类的内部,定义友元运算符重载函数的格式如下: friend 函数类型 operator 运算符(形参表&a…...

从0开始实现es6 promise类
主要由基础实现和静态类的实现两部分组成。 1 基础实现(不含静态类) 1.1 使用类实现完成构造函数 实现代码如下,构造函数传入一个回调函数,定义resolve和reject函数,将两个函数作为参数执行回调函数。 // 1. 使用类实…...