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

学到这里终于能用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
......................
......................
......................
......................
......................
......................
......................
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值