您的位置:首页 > 编程语言 > Ruby

【Ruby学习笔记】6:模拟OS文件操作与管理

2018-01-29 00:47 435 查看
学到这里终于能用Ruby写点程序了,拿操作系统上机作业练习一下。

要求设计一个模拟的文件系统,有主文件目录(MFD)用户文件目录(UFD)。我用一个文件去模拟磁盘,把这两项内容都写进磁盘里(这些断电不丢失的肯定需要在外存存着)。

不需要真的实现读写,只要更改文件读写指针,而且这些读写指针一断电自然就没有了(电子书’书签’那种不算),用一个Ruby数组(其实也可以只用后面的哈希表)作为打开文件目录,但是我只用这个记录文件名,保护码放在外存不会变的(这题不用做chmod这种命令),然后文件指针我用一个哈希表来存(这个比数组快很多吧),而且只在真正用到文件指针的时候才去往哈希表里添加东西。

Ruby的哈希表有一大好处就是能指定一个默认值,如果查询哈希表项不存在的时候会返回那个值,正好在这题里设置默认值是0,很方便。

Ruby的
case-when-..-else-end
很好用,做保护码解析的时候太方便了。

假设只有10个用户,每个用户最多只能建立10个文件,每个文件的最大长度是99(这个长度其实很好加,但是懒得去作变长行尾的判断读入了)。

代码

#!/usr/bin/ruby

#一开始打算把文件目录项做成class的,结果发现实际要用的就两个成员
#而且文件名(或者文件号)都是一种候选键
#所以完全可以用哈希表{key-value}即{文件名-指针位置}
#打开文件目录项,一定是在内存中的
#所以不写进模拟磁盘的./MyFileSys里
#这个OpenFile类没有被使用
class OpenFile
def initialize(name,pro,p=0)
@name=name #打开文件号(名)
@rw_point=p #读写指针,普通模式就是从头开始
end

#设置读写指针
def setPoint(p)
@rw_point=p
end

#获取读写指针
def getPoint()
return @rw_point
end
end

#登录的用户,为了OOP,方法都在这里
class USR
#因为各个用户用的文件系统都一样,这里显然用类变量
@@ept="......................" #表示此处没有文件,删除用
#这两个就要用实例变量了,在类外定义才不是nil
#但是我用实例变量总提示是nil(空)!!!这是个遗留问题
#没办法暂时只能用类变量
@@open_array=Array.new() #打开文件目录,初始为空
@@hash_tab=Hash.new(0) #哈希表,用来关联文件名->指针位置,默认值是0
#new是通过调用initialize产生了一个对象实例
def initialiaze()
#定义一些和具体用户有关的需要及时用上的实例变量
@usr_name=nil #用户名
@UFD_point=nil #用户文件目录指针
end

#设置用户名
def setName(name)
@usr_name=name
end

#设置其用户文件目录的行号
def setPoint(p)
@UFD_point=p
end

#获取其用户文件目录的行号
def getPoint()
return @UFD_point
end

#展示文件目录
def showDic()
puts("--下面是你的UFD(用户文件目录)--")
puts("文件名   保护码  文件长度")
file=File.open("./MyFileSys","r")
#文件指针向后偏移的同时正好存下偏移量@bais
file.seek(@bais=10*15+(@UFD_point-11)*23)
@str=file.sysread(10*23)
puts(@str)
file.close()
end

#查找能放置新文件的表项
def findNew()
file=File.open("./MyFileSys","r")
file.seek(@bais)
for i in 0..9 do
line=file.sysread(23)
if line[0..3]=="....": #说明这里有文件了
file.close() #return之前别忘了close()文件
return i
end
end
file.close()
return -1
end

#在num位置创建文件filename的方法
def createFile(num,filename)
ok=false
until true==ok do
ok=true
puts(">>请输入新文件的保护码:")
pro=gets().chomp()
case pro
when "rwx","111","7"
puts("将被解析为#{pro="rwx"}")
when "rw-","110","6"
puts("将被解析为#{pro="rw-"}")
when "r-x","101","5"
puts("将被解析为#{pro="r-x"}")
when "r--","100","4"
puts("将被解析为#{pro="r--"}")
when "-wx","011","11","3"
puts("将被解析为#{pro="-wx"}")
when "-w-","010","10","2"
puts("将被解析为#{pro="-w-"}")
when "--x","001","01","1"
puts("将被解析为#{pro="--x"}")
when "---","000","00","0"
puts("将被解析为#{pro="---"}")
else
ok=false
puts("[ERROR]不合法的保护码")
end #end case
end #end until
#只往UFD用户文件目录里写
file=File.open("./MyFileSys","r+") #按文件指针写的读写模式
file.seek(@bais+23*num)
file.syswrite(filename+" "*(10-filename.length)+pro+" "*(10-pro.length)+"00") #新建的文件大小是0字节
file.close()
end

#判断以及调用createFile(num)方法
def createJudge(num)
ok=false
until true==ok do
ok=true #在判断中发现不符合条件的就置false
puts(">>请输入文件的名称:")
filename=gets().chomp()
if filename.length==0:
puts("[ERROR]未输入")
ok=false
elsif filename.length>9:
puts("[ERROR]太长了")
ok=false
elsif filename.length>filename.gsub(' ','').length:
puts("[ERROR]有空白字符")
ok=false
else #判断有没有文件重名
file=File.open("./MyFileSys","r")
file.seek(@bais)
for i in 0..9 do
line=file.sysread(23)
if /^#{filename} /.match(line):
puts("[ERROR]存在文件重名")
ok=false
break #这里仅仅是跳出for
end #end if
end #end for
file.close()
end #end if-elsif-...-else
end #end until
createFile(num,filename) #出了循环体才在这里创建文件
puts("[SUCCESS]创建文件成功")
end #end def

#判断并删除文件,这个设计成不会一直循环要求
#而创建文件设计成这样的原因是
#如在图形界面下创建,在用户为文件命名之前就真的创建好了
def delFile()
ok=-1 #表示还没有删除文件
puts(">>请输入要删除的文件的名称:")
filename=gets().chomp()
file=File.open("./MyFileSys","r+")
file.seek(@bais)
for i in 0..9 do
line=file.sysread(23)
if /^#{filename} /.match(line): #当发现这个文件时
puts(line)
ok=i #在循环里不能做,传给ok拿到外面做
break #这里仅仅是跳出for
end #end if
end #end for
if -1==ok:
puts("[ERROR]不存在的文件")
else
#实在无奈!不知道为什么总是追加模式!只能重新打开文件
file.close()
file=File.open("./MyFileSys","r+")
file.seek(@bais+23*ok,IO::SEEK_SET) #指针到达该行行首
#puts(file.sysread(23))
file.syswrite(@@ept) #用一堆"."覆盖掉即可
#从打开目录表中删除(即考虑万一没关闭就删的情况)
@@open_array.delete(filename)
#哈希表中应删除之(不再存其读写指针位置)
@@hash_tab.delete(filename)
puts("[SUCCESS]删除成功")
end
file.close()
end

#打开文件,也设计成不会一直循环要求
def openFile()
ok=-1 #表示还没有打开文件
puts(">>请输入你要打开的文件名:")
filename=gets().chomp()
file=File.open("./MyFileSys","r+")
file.seek(@bais)
for i in 0..9 do
line=file.sysread(23)
if /^#{filename} /.match(line): #当发现这个文件时
if @@open_array.include?(filename)==true: #如果已经打开了
puts("[ERROR]已经打开过了")
ok=i #把i传到外面
break #这里仅仅是跳出for
end

@@open_array.push(filename) #添加进打开目录表中
puts("[SUCCESS]"+filename+"成功打开了")
ok=i #把i传到外面
break #这里仅仅是跳出for
end #end if
end #end for
if -1==ok:
puts("[ERROR]不存在的文件")
end
file.close()
end

#关闭文件,也设计成不会一直循环要求
def closeFile()
ok=-1 #表示还没有关闭文件
puts(">>请输入你要关闭的文件名:")
filename=gets().chomp()
file=File.open("./MyFileSys","r+")
file.seek(@bais)
for i in 0..9 do
line=file.sysread(23)
if /^#{filename} /.match(line): #当发现这个文件时
if @@open_array.include?(filename)!=true: #如果已经关闭了
puts("[ERROR]这个文件没有打开,不需关闭")
ok=i #把i传到外面
break #这里仅仅是跳出for
end

@@open_array.delete(filename) #从打开目录表中删除
@@hash_tab[filename]=0 #关闭文件后,读写指针显然要清到0
puts("[SUCCESS]"+filename+"成功关闭了")
ok=i #把i传到外面
break #这里仅仅是跳出for
end #end if
end #end for
if -1==ok:
puts("[ERROR]不存在的文件")
end
file.close()
end

#读文件
def readFile()
ok=-1 #表示还没有读文件
puts(">>请输入你要读的文件名:")
filename=gets().chomp()
file=File.open("./MyFileSys","r")
file.seek(@bais)
for i in 0..9 do
line=file.sysread(23)
if /^#{filename} /.match(line): #当发现这个文件时
if @@open_array.include?(filename)!=true: #如果已经关闭了
puts("[ERROR]这个文件还没打开,不能读")
ok=i #把i传到外面
break #这里仅仅是跳出for
elsif line[10..10]=="-" #如果没有读权限
puts("[ERROR]你没有读取它的权限")
ok=i #把i传到外面
break #这里仅仅是跳出for
end

puts(">>请输入你要读的字节数")
readnum=gets().chomp().to_i()
#把要读的字节数加到当前文件指针位置上
readnum+=@@hash_tab[filename]
allnum=line[20..21].to_i() #在外存查总字节数
#(仅当第一次需要读或者写时)存进哈希表里
@@hash_tab[filename]=readnum>allnum ? allnum : readnum
if readnum>allnum:
puts("[SUCCESS]现在,已经读到文件尾(#{allnum})")
else
puts("[SUCCESS]现在,文件指针在#{readnum}处")
end
ok=i #把i传到外面
break #这里仅仅是跳出for
end #end if
end #end for
if -1==ok:
puts("[ERROR]不存在的文件")
end
file.close()
end

#写文件
def writeFile()
ok=-1 #表示还没有写文件
puts(">>请输入你要写的文件名:")
filename=gets().chomp()
file=File.open("./MyFileSys","r+")
file.seek(@bais)
for i in 0..9 do
line=file.sysread(23)
if /^#{filename} /.match(line): #当发现这个文件时
if @@open_array.include?(filename)!=true: #如果已经关闭了
puts("[ERROR]这个文件还没打开,不能写")
ok=i #把i传到外面
break #这里仅仅是跳出for
elsif line[11..11]=="-" #如果没有写权限
puts("[ERROR]你没有写入它的权限")
ok=i #把i传到外面
break #这里仅仅是跳出for
end

puts(">>请输入你要从哪个位置开始写")
position=gets().chomp().to_i()
allnum=line[20..21].to_i() #在外存查总字节数
if position>allnum or position<0:
puts("[ERROR]非法的位置")
ok=i #把i传到外面
break #这里仅仅是跳出for
end

#(仅当第一次需要读或者写时)存进哈希表里
@@hash_tab[filename]=position
puts("[SUCCESS]现在,文件指针在#{allnum}处")

puts(">>请输入你要写的内容")
writenum=gets().chomp().length() #模拟,只记录长度
if writenum+@@hash_tab[filename]>99: #假定每个文件只能写99字节
puts("[ERROR]余下空间不允许你从#{position}位置写入这么多的内容")
ok=i #把i传到外面
break #这里仅仅是跳出for
end

#更新写好后文件指针的位置
@@hash_tab[filename]=writenum+@@hash_tab[filename]
if @@hash_tab[filename]>allnum: #写完超过原来的长度
#应该会和前面出现一样的bug!只能重新打开文件
file.close()
file=File.open("./MyFileSys","r+")
file.seek(@bais+23*i,IO::SEEK_SET) #指针到达该行行首
#更新UFD用户文件目录里的长度
file.syswrite(line[0..19]+@@hash_tab[filename].to_s())
puts("[SUCCESS]现在,文件指针在文件尾(#{@@hash_tab[filename]})")
else
puts("[SUCCESS]现在,文件指针在#{@@hash_tab[filename]}处")
end
ok=i #把i传到外面
break #这里仅仅是跳出for
end #end if
end #end for
if -1==ok:
puts("[ERROR]不存在的文件")
end
file.close()
end

end #end class

#建立用户对象
usr=USR.new()

#登录验证
find=false
until true==find do #循环直到读入正确的姓名
print(">>请输入用户名: ")
name=gets().chomp() #读进来的名字要去掉换行符
file=File.open("./MyFileSys","r")
file.each do |line|
#为了防止pika能登录pikachu这种bug,要在名字后接个空格
#为了防止kachu能登录pikachu这种bug,要在名字前匹配行首
if /^#{name} /.match(line): #正则匹配
puts line[10,4]
#截取后面的目录区指针,字符串转换成整数再set
usr.setPoint(line[10,4].to_i())
usr.setName(name)
find=true
break
end
end

if false==find:
puts("[ERROR]不存在叫这个名字的用户")
end
file.close()
end

#登录后展示一下
usr.showDic()

#接收命令,作出响应
while true do
print(">>请输入命令的名称: ")
cmd=gets().chomp() #读进来的命令要去掉换行符
case cmd
when "show"
usr.showDic()
when "bye"
usr.showDic()
puts("再见!")
break
when "create"
if -1==(num=usr.findNew()):
puts("[ERROR]你已经用完10个文件了")
else
usr.createJudge(num)
end
when "delete"
usr.delFile()
when "open"
usr.openFile()
when "close"
usr.closeFile()
when "read"
usr.readFile()
when "write"
usr.writeFile()
else
puts("[ERROR]命令的名称不正确!")
puts("必须是以下中的一个: create,delete,open(close,read,write),bye")
end
end


运行测试1

这个测试的是功能和逻辑限制。

[lzh@hostlzh Ruby]$ ruby sy4.rb
>>请输入用户名: pikachu
0051
--下面是你的UFD(用户文件目录)--
文件名   保护码  文件长度
Pika1     rwx       11
Pika2     rwx       22
Pika3     --x       17
......................
......................
......................
......................
......................
......................
......................
>>请输入命令的名称: read
>>请输入你要读的文件名:
Pika3
[ERROR]这个文件还没打开,不能读
>>请输入命令的名称: open
>>请输入你要打开的文件名:
Pika3
[SUCCESS]Pika3成功打开了
>>请输入命令的名称: read
>>请输入你要读的文件名:
Pika3
[ERROR]你没有读取它的权限
>>请输入命令的名称: open
>>请输入你要打开的文件名:
Pika2
[SUCCESS]Pika2成功打开了
>>请输入命令的名称: read
>>请输入你要读的文件名:
Pika2
>>请输入你要读的字节数
50
[SUCCESS]现在,已经读到文件尾(22)
>>请输入命令的名称: delete
>>请输入要删除的文件的名称:
Pika2
Pika2     rwx       22
[SUCCESS]删除成功
>>请输入命令的名称: show
--下面是你的UFD(用户文件目录)--
文件名   保护码  文件长度
Pika1     rwx       11
......................
Pika3     --x       17
......................
......................
......................
......................
......................
......................
......................
>>请输入命令的名称: create
>>请输入文件的名称:
LiuSB
>>请输入新文件的保护码:
101
将被解析为r-x
[SUCCESS]创建文件成功
>>请输入命令的名称: show
--下面是你的UFD(用户文件目录)--
文件名   保护码  文件长度
Pika1     rwx       11
LiuSB     r-x       00
Pika3     --x       17
......................
......................
......................
......................
......................
......................
......................
>>请输入命令的名称: write
>>请输入你要写的文件名:
Pika3
[ERROR]你没有写入它的权限
>>请输入命令的名称: write
>>请输入你要写的文件名:
Pika1
[ERROR]这个文件还没打开,不能写
>>请输入命令的名称: open
>>请输入你要打开的文件名:
Pika1
[SUCCESS]Pika1成功打开了
>>请输入命令的名称: write
>>请输入你要写的文件名:
Pika1
>>请输入你要从哪个位置开始写
7
[SUCCESS]现在,文件指针在11处
>>请输入你要写的内容
wo shi da sha bi
[SUCCESS]现在,文件指针在文件尾(23)
>>请输入命令的名称: show
--下面是你的UFD(用户文件目录)--
文件名   保护码  文件长度
Pika1     rwx       23
LiuSB     r-x       00
Pika3     --x       17
......................
......................
......................
......................
......................
......................
......................
>>请输入命令的名称: bye
--下面是你的UFD(用户文件目录)--
文件名   保护码  文件长度
Pika1     rwx       23
LiuSB     r-x       00
Pika3     --x       17
......................
......................
......................
......................
......................
......................
......................
再见!
[lzh@hostlzh Ruby]$


运行测试2

这个测试的是对输入的鲁棒性。

[lzh@hostlzh Ruby]$ ruby sy4.rb
>>请输入用户名: dsaksafwk
[ERROR]不存在叫这个名字的用户
>>请输入用户名:
[ERROR]不存在叫这个名字的用户
>>请输入用户名: zhihao
[ERROR]不存在叫这个名字的用户
>>请输入用户名: liuzhi
[ERROR]不存在叫这个名字的用户
>>请输入用户名: liuzhihao
0011
--下面是你的UFD(用户文件目录)--
文件名   保护码  文件长度
Liu1      rwx       11
Liu2      r-x       20
Liu3      r--       17
WoShiSB   r-x       00
......................
......................
......................
......................
......................
......................
>>请输入命令的名称: sb
[ERROR]命令的名称不正确!
必须是以下中的一个: create,delete,open(close,read,write),bye
>>请输入命令的名称: create
>>请输入文件的名称:
Liu1
[ERROR]存在文件重名
>>请输入文件的名称:
Liu File
[ERROR]有空白字符
>>请输入文件的名称:
666666666666666666666666
[ERROR]太长了
>>请输入文件的名称:

[ERROR]未输入
>>请输入文件的名称:
niubi
>>请输入新文件的保护码:
-10
[ERROR]不合法的保护码
>>请输入新文件的保护码:
1000
[ERROR]不合法的保护码
>>请输入新文件的保护码:
dsa
[ERROR]不合法的保护码
>>请输入新文件的保护码:
5
将被解析为r-x
[SUCCESS]创建文件成功
>>请输入命令的名称: show
--下面是你的UFD(用户文件目录)--
文件名   保护码  文件长度
Liu1      rwx       11
Liu2      r-x       20
Liu3      r--       17
WoShiSB   r-x       00
niubi     r-x       00
......................
......................
......................
......................
......................
>>请输入命令的名称: delete
>>请输入要删除的文件的名称:
WoShi
[ERROR]不存在的文件
>>请输入命令的名称: delete
>>请输入要删除的文件的名称:
WoShiSB
WoShiSB   r-x       00
[SUCCESS]删除成功
>>请输入命令的名称: delete
>>请输入要删除的文件的名称:
Liu1
Liu1      rwx       11
[SUCCESS]删除成功
>>请输入命令的名称: show
--下面是你的UFD(用户文件目录)--
文件名   保护码  文件长度
......................
Liu2      r-x       20
Liu3      r--       17
......................
niubi     r-x       00
......................
......................
......................
......................
......................
>>请输入命令的名称: open
>>请输入你要打开的文件名:
Liu1
[ERROR]不存在的文件
>>请输入命令的名称: open
>>请输入你要打开的文件名:
niubi
[SUCCESS]niubi成功打开了
>>请输入命令的名称: read
>>请输入你要读的文件名:
niubi
>>请输入你要读的字节数
999999999999999999
[SUCCESS]现在,已经读到文件尾(0)
>>请输入命令的名称: open
>>请输入你要打开的文件名:
Liu2
[SUCCESS]Liu2成功打开了
>>请输入命令的名称: read
>>请输入你要读的文件名:
Liu2
>>请输入你要读的字节数
0
[SUCCESS]现在,文件指针在0处
>>请输入命令的名称: read
>>请输入你要读的文件名:
Liu2
>>请输入你要读的字节数
17
[SUCCESS]现在,文件指针在17处
>>请输入命令的名称: bye
--下面是你的UFD(用户文件目录)--
文件名   保护码  文件长度
......................
Liu2      r-x       20
Liu3      r--       17
......................
niubi     r-x       00
......................
......................
......................
......................
......................
再见!
[lzh@hostlzh Ruby]$


文件系统格式

MyFileSys
文件的格式,前面短的是MFD主文件目录,后面每行23个字符(算上换行)的是UFD用户文件目录。

liuzhihao 0011
tom       0021
jerry     0031
john      0041
pikachu   0051
eclipse   0061
lzh       0071
kati      0081
apache    0091
yast      0101
Liu1      rwx       11
Liu2      r-x       20
Liu3      r--       17
......................
......................
......................
......................
......................
......................
......................
Tom1      rwx       18
Tom2      r-x       10
Tom3      r--       27
......................
......................
......................
......................
......................
......................
......................
Jry1      r-x       13
Jry2      rwx       10
Jry3      r--       14
......................
......................
......................
......................
......................
......................
......................
John1     rwx       11
John2     r-x       20
John3     r--       17
......................
......................
......................
......................
......................
......................
......................
Pika1     rwx       11
Pika2     rwx       22
Pika3     --x       17
......................
......................
......................
......................
......................
......................
......................
Ecl1      r-x       11
Ecl2      r-x       20
Ecl3      r--       17
......................
......................
......................
......................
......................
......................
......................
Lzh1      rwx       11
Lzh2      r-x       20
Lzh3      -w-       17
......................
......................
......................
......................
......................
......................
......................
Kati1     rwx       11
Kati2     r-x       20
Kati3     r--       17
......................
......................
......................
......................
......................
......................
......................
Apache1   rwx       11
Apache2   r-x       20
Apache3   r--       17
......................
......................
......................
......................
......................
......................
......................
Yast1     rwx       11
Yast2     r-x       20
Yast3     r--       17
......................
......................
......................
......................
......................
......................
......................
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: