1. 程式人生 > >c++實現的十分簡易檔案系統

c++實現的十分簡易檔案系統

課題作業任務:在記憶體中開闢一個100M的空間作為磁碟空間,在此上模擬建立一個檔案管理系統。

本系統已經實現的相關操作

所有操作都不支援從根目錄索引,即都只能訪問當前目錄下的檔案,不能用/a/b越級訪問

1)目錄相關操作

列出目錄下所有檔案:ls

切換目錄:cd newdir

顯示目錄:pwd

建立目錄:mkdir dirName

刪除目錄和目錄下所有檔案:rmdir dirName

修改目錄名或檔名:mv oldName newName

 2)檔案相關操作

建立檔案(大小以1KB為單位):touch filename fileSize

刪除檔案:rm filename

從上一次後讀取size位元組的檔案內容:read fileName size

從頭開始讀取size位元組的檔案內容:reread fileName size

從檔案尾寫入內容:write fileName content

清空檔案,從頭寫入:rewrite fileName content

 3)系統操作

使用命令幫助:help

退出系統:quit

全部程式碼大概有600多行,是一個非常簡單的實現,不適合想要深入瞭解的人觀看,網上有其他的功能較為強大實現,程式碼都在上千,比較具有學習的價值。

點選開啟連結(很強大,我自己也還沒有仔細看)

點選開啟連結(程式碼好像有點問題)

1. 系統層次結構


系統介面:是該檔案系統提供給使用者可以使用的命令介面,如ls,mkdir,touch等

檔案管理:是系統對於檔案和目錄層次的管理的,規定了FCB結構,目錄結構等,包含了對介面的實現。

磁碟管理:是系統最底層直接對記憶體空間的管理,如磁碟空閒空間管理,磁碟空間的分配方式等。

2. 磁碟管理

盤塊大小:以1KB的空間作為系統盤塊,用以分配的基本單位

分配方式:系統採用連續分配的方式,在磁碟上劃定要求的連續盤塊分配給檔案使用

空間管理:系統採用位示圖的方法,標記了磁碟上所有盤塊的使用情況。

    使用systemStartAddr標記了整個系統的起始地址,以1KB作為盤塊劃分,所以本系統共有100K個盤塊。位示圖用char[]表示,所以位示圖大小為100KB。位示圖儲存在系統起始位置,在系統初始化時,0~99號盤塊預設被儲存位示圖使用。

磁碟向檔案管理提供的介面說明:

DiskOperate.h
#ifndef DISKOPERATE_H_INCLUDED
#define DISKOPERATE_H_INCLUDED
//磁碟操作介面
#define system_size 100*1024*1024   //系統大小
#define block_szie 1024 //盤塊大小
#define block_count system_size/block_szie //系統盤塊數目

//初始化系統
void initSystem();
//磁碟分配
int getBlock(int blockSize) ;
//獲得盤塊的實體地址
char* getBlockAddr(int blockNum);
//獲得實體地址的盤塊號
int getAddrBlock(char* addr);
//釋放盤塊、
int releaseBlock(int blockNum, int blockSize);
//退出系統
void exitSystem();
#endif // DISKOPERATE_H_INCLUDED

3.檔案層次管理說明

FileOperate.h (部分內容)
//目錄項結構:
struct dirUnit{
    char fileName[59];  //檔名
    char type;  //檔案型別,0目錄, 1檔案
    int startBlock; //FCB起始盤塊
};
//一個目錄項包含了檔名和檔案型別,當檔案為目錄時,起始盤塊指示了目錄表所在的盤塊號,當檔案為檔案時,起始盤塊指示了FCB所在的盤塊號。
#define dirTable_max_size 15    //目錄表項最大值
//目錄表結構:
struct dirTable {
    int dirUnitAmount;//目錄項數目
    dirUnit dirs[dirTable_max_size];//目錄項列表
};
/*
本系統規定一個目錄表只佔用一個盤塊,一個目錄項大小為64B,所以一個目錄表中最多可含15個目錄項,dirUnitAmount記錄每個目錄表中已含有的目錄項數目。系統在初始化時,會自動生成一個空的根目錄表存放於磁碟中,作為使用者的初始位置,使用者所有的目錄和檔案都這個表為根進行樹狀目錄結構的展開。
當建立一個目錄表時,系統會自動為目錄表加上一項名為”..”的目錄項,指示父目錄表的位置。
*/

//FCB結構:
struct FCB {
    int blockNum;   //檔案資料起始盤塊號
    int fileSize;   //檔案大小,盤塊為單位
    int dataSize;   //已寫入的內容大小,位元組為單位
    int readptr;    //讀指標,位元組為單位
    int link;   //檔案連結數
};
/*
	檔案控制塊包含了檔案資料的起始位置和大小。dataSize,readptr是為檔案的讀寫操作而準備的,記錄檔案已寫入的內容長度(不可超過檔案大小),和當前讀取的位置。Link記錄了檔案的連結數,用於檔案的共享,當檔案的連結數為0時,系統可以回收檔案的空間。同樣的,一個FCB大小為20B,但也用一個盤塊儲存。
	由於採用的是連續分配方式,所以系統規定檔案被建立時,必須給出檔案的大小,而且後期也不能修改檔案的大小。
*/

看到這裡,如果你有修過計算機系統的話,你就會發現我採取了一種最簡單的實現方式,都說了十分簡易嘛~~

系統用char[]陣列作為位示圖儲存了每一個盤塊的使用狀態,而且採取了連續分配的方式,對於目錄表和FCB都規定直接使用一個盤塊,檔案又是規定好大小不能擴充套件的,所以實現起來減少了很多FAT,索引表,那些離散分配所需的連線方式,單純練手的話,還是十分簡易的。

具體實現

1. 磁碟管理的實現

這部分比較簡單,程式碼備註也很詳細了,可以直接看程式碼

DiskOperate.cpp

#include"DiskOperate.h"

#include<stdio.h>
#include<stdlib.h>



char* systemStartAddr;  //系統起始地址

//初始化系統
void initSystem()
{
    //建立空間
    systemStartAddr = (char*)malloc(system_size * sizeof(char));
    //初始化盤塊的位示圖
    for(int i=0; i<block_count; i++)
        systemStartAddr[i] = '0';
    //用於存放位示圖的空間已被佔用
    int bitMapSize = block_count * sizeof(char) / block_szie;//位示圖佔用盤塊數:100
    for(int i=0; i<bitMapSize; i++)//從零開始分配
        systemStartAddr[i] = '1';   //盤塊已被使用
}
//退出系統
void exitSystem()
{
    free(systemStartAddr);
}
//磁碟分配
int getBlock(int blockSize)
{
    int startBlock = 0;
    int sum=0;
    for(int i=0; i<block_count; i++)
    {
        if(systemStartAddr[i] == '0')//可用盤塊
        {
            if(sum == 0)//剛開始,設定開始盤塊號
                startBlock = i;
            sum++;
            if(sum == blockSize)//連續盤塊是否滿足需求
            {
                //滿足分配,置1
                for(int j=startBlock; j<startBlock+blockSize; j++)
                    systemStartAddr[j] = '1';
                return startBlock;
            }

        }
        else//已被使用,連續已經被打斷
            sum = 0;
    }
    printf("not found such series memory Or memory is full\n");
    return -1;
}
//獲得盤塊的實體地址
char* getBlockAddr(int blockNum)
{
    return systemStartAddr + blockNum * block_szie; //偏移量單位為位元組
}
//獲得實體地址的盤塊號
int getAddrBlock(char* addr)
{
    return (addr - systemStartAddr)/block_szie;
}
//釋放盤塊、
int releaseBlock(int blockNum, int blockSize)
{
    int endBlock = blockNum + blockSize;
    //修改位示圖盤塊的位置為0
    for(int i=blockNum; i<endBlock; i++)
        systemStartAddr[i] = '0';
    return 0;
}

2.檔案管理

首先說明全域性變數
dirTable* rootDirTable; //根目錄
dirTable* currentDirTable;  //當前所在目錄位置
char path[200]; //儲存當前絕對路徑
結構體定義可以見上面檔案管理

1)建立檔案:touch fileName size

建立檔案的過程可以為:

為檔案控制塊申請空間->為檔案資料申請空間->建立FCB控制塊->在當前目錄新增相關的目錄項描述

//建立檔案
int creatFile(char fileName[], int fileSize)
{
    //檢測檔名字長度
    if(strlen(fileName) >= 59)
    {
        printf("file name too long\n");
        return -1;
    }
    //獲得FCB的空間
    int FCBBlock = getBlock(1);
    if(FCBBlock == -1)
        return -1;
    //獲取檔案資料空間
    int FileBlock = getBlock(fileSize);
    if(FileBlock == -1)
        return -1;
    //建立FCB
    if(creatFCB(FCBBlock, FileBlock, fileSize) == -1)
        return -1;
    //新增到目錄項
    if(addDirUnit(currentDirTable, fileName, 1, FCBBlock) == -1)
        return -1;

    return 0;
}
//建立FCB
int creatFCB(int fcbBlockNum, int fileBlockNum, int fileSize)
{
    //找到fcb的儲存位置
    FCB* currentFCB = (FCB*) getBlockAddr(fcbBlockNum);
    currentFCB->blockNum = fileBlockNum;//檔案資料起始位置
    currentFCB->fileSize = fileSize;//檔案大小
    currentFCB->link = 1;//檔案連結數
    currentFCB->dataSize = 0;//檔案已寫入資料長度
    currentFCB->readptr = 0;//檔案讀指標
    return 0;
}
//新增目錄項
int addDirUnit(dirTable* myDirTable, char fileName[], int type, int FCBBlockNum)
{
    //獲得目錄表
    int dirUnitAmount = myDirTable->dirUnitAmount;
    //檢測目錄表示是否已滿
    if(dirUnitAmount == dirTable_max_size)
    {
        printf("dirTables is full, try to delete some file\n");
        return -1;
    }

    //是否存在同名檔案
    if(findUnitInTable(myDirTable, fileName) != -1)
    {
        printf("file already exist\n");
           return -1;
    }
    //構建新目錄項
    dirUnit* newDirUnit = &myDirTable->dirs[dirUnitAmount];
    myDirTable->dirUnitAmount++;//當前目錄表的目錄項數量+1
    //設定新目錄項內容
    strcpy(newDirUnit->fileName, fileName);
    newDirUnit->type = type;
    newDirUnit->startBlock = FCBBlockNum;

    return 0;
}
//從目錄中查詢目錄專案
int findUnitInTable(dirTable* myDirTable, char unitName[])
{
    //獲得目錄表
    int dirUnitAmount = myDirTable->dirUnitAmount;
    int unitIndex = -1;
    for(int i=0; i<dirUnitAmount; i++)//查詢目錄項位置
       if(strcmp(unitName, myDirTable->dirs[i].fileName) == 0)
            unitIndex = i;
    return unitIndex;
}
2)刪除檔案:rm fileName

刪除檔案的流程可以分為:

查詢檔案在當前目錄的目錄項描述內容->得到FCB的描述內容->釋放FCB空間和檔案資料空間->從目錄表中刪除檔案的目錄項

(也就是說,在這裡其實只是把使用者能得到檔案的索引刪除,而檔案的內容在沒有被覆蓋之前依舊是存在的)

//刪除檔案
int deleteFile(char fileName[])
{
    //忽略系統的自動建立的父目錄
    if(strcmp(fileName, "..") == 0)
    {
        printf("can't delete ..\n");
        return -1;
    }
    //查詢檔案的目錄項位置
    int unitIndex = findUnitInTable(currentDirTable, fileName);
    if(unitIndex == -1)
    {
        printf("file not found\n");
        return -1;
    }
    dirUnit myUnit = currentDirTable->dirs[unitIndex];
    //判斷型別
    if(myUnit.type == 0)//目錄
    {
        printf("not a file\n");
        return -1;
    }
    int FCBBlock = myUnit.startBlock;
    //釋放記憶體
    releaseFile(FCBBlock);
    //從目錄表中剔除
    deleteDirUnit(currentDirTable, unitIndex);
    return 0;
}
//釋放檔案記憶體
int releaseFile(int FCBBlock)
{
    FCB* myFCB = (FCB*)getBlockAddr(FCBBlock);
    myFCB->link--;  //連結數減一
    //無連結,刪除檔案
    if(myFCB->link == 0)
    {
        //釋放檔案的資料空間
        releaseBlock(myFCB->blockNum, myFCB->fileSize);
    }
    //釋放FCB的空間
    releaseBlock(FCBBlock, 1);
    return 0;
}
//刪除目錄項
int deleteDirUnit(dirTable* myDirTable, int unitIndex)
{
    //遷移覆蓋
    int dirUnitAmount = myDirTable->dirUnitAmount;
    for(int i=unitIndex; i<dirUnitAmount-1; i++)
    {
        myDirTable->dirs[i] = myDirTable->dirs[i+1];
    }
    myDirTable->dirUnitAmount--;
    return 0;
}


3)目錄的刪除和建立

目錄的建立和刪除與檔案的操作大致相同,建立目錄時,目錄表項的startBlock不是FCB而是指目錄的存放位置,而且還要自動為其新增多一個父目錄項“..”,用於跳轉。刪除目錄時需要注意是否遞迴刪除目錄下的所有檔案和目錄。詳請看最後的完整程式碼,這裡不專門複述。

4)切換目錄: cd dirName

cd指令,主要就是讓當前目錄currentDirTable指向新的目錄盤塊地址就可以了,目錄盤塊地址可以在當前目錄表的表項中找到。

//切換目錄
int changeDir(char dirName[])
{
    //目錄項在目錄位置
    int unitIndex = findUnitInTable(currentDirTable, dirName);
    //不存在
    if(unitIndex == -1)
    {
        printf("file not found\n");
        return -1;
    }
    if(currentDirTable->dirs[unitIndex].type == 1)
    {
        printf("not a dir\n");
        return -1;
    }
    //修改當前目錄
    int dirBlock = currentDirTable->dirs[unitIndex].startBlock;
    currentDirTable = (dirTable*)getBlockAddr(dirBlock);
    //修改全域性絕對路徑path
    if(strcmp(dirName, "..") == 0)
    {
        //回退絕對路徑
        int len = strlen(path);
        for(int i=len-2;i>=0;i--)
            if(path[i] == '\\')
            {
                path[i+1]='\0';
                break;
            }
    }else {
        strcat(path, dirName);
        strcat(path, "\\");
    }

    return 0;
}



5)讀檔案:read fileName size

主要就是獲得檔案的初始地址,然後根據需要的讀取長度size和當前的讀指標進行輸出,如果遇到檔案尾則輸出#,讀寫的單位都是位元組

//讀檔案 read
int read(char fileName[], int length)
{
    int unitIndex = findUnitInTable(currentDirTable, fileName);
    if(unitIndex == -1)
    {
        printf("file no found\n");
        return -1;
    }
    //控制塊
    int FCBBlock = currentDirTable->dirs[unitIndex].startBlock;
    FCB* myFCB = (FCB*)getBlockAddr(FCBBlock);
    doRead(myFCB, length);
    return 0;
}
//執行讀操作 
int doRead(FCB* myFCB, int length)
{
    //讀資料
    int dataSize = myFCB->dataSize;
    char* data = (char*)getBlockAddr(myFCB->blockNum);
    //在不超出資料長度下,讀取指定長度的資料
    for(int i=0; i<length && myFCB->readptr < dataSize; i++, myFCB->readptr++)
    {
        printf("%c", *(data+myFCB->readptr));
    }
    if(myFCB->readptr == dataSize)//讀到檔案末尾用#表示
        printf("#");
    //換行美觀
    printf("\n");
    return 0;
}
reread就是先把讀指標readptr置為0,然後執行讀操作。

6)寫檔案操作:write fileName content

和讀檔案差不多,不過是根據當前檔案的資料長度,在檔案末尾給檔案以位元組的形式賦值上輸入的content內容,資料長度不能超過檔案長度

//寫檔案,從末尾寫入 write
int write(char fileName[], char content[])
{
    int unitIndex = findUnitInTable(currentDirTable, fileName);
    if(unitIndex == -1)
    {
        printf("file no found\n");
        return -1;
    }
    //控制塊
    int FCBBlock = currentDirTable->dirs[unitIndex].startBlock;
    FCB* myFCB = (FCB*)getBlockAddr(FCBBlock);
    doWrite(myFCB, content);
    return 0;
}
//執行寫操作
int doWrite(FCB* myFCB, char content[])
{
    int contentLen = strlen(content);
    int fileSize = myFCB->fileSize * block_szie;
    char* data = (char*)getBlockAddr(myFCB->blockNum);
    //在不超出檔案的大小的範圍內寫入
    for(int i=0; i<contentLen && myFCB->dataSize<fileSize; i++, myFCB->dataSize++)
    {
        *(data+myFCB->dataSize) = content[i];
    }
    if(myFCB->dataSize == fileSize)
        printf("file is full,can't write in\n");
    return 0;
}

7)展示目錄下所有檔案:ls

對目錄表進行遍歷,同時根據檔案型別做出不同的輸出就可以了

//展示當前目錄 ls
void showDir()
{
    int unitAmount = currentDirTable->dirUnitAmount;
    printf("total:%d\n", unitAmount);
    printf("name\ttype\tsize\tFCB\tdataStartBlock\n");
    //遍歷所有表項
    for(int i=0; i<unitAmount; i++)
    {
        //獲取目錄項
        dirUnit unitTemp = currentDirTable->dirs[i];
        printf("%s\t%d\t", unitTemp.fileName, unitTemp.type);
        //該表項是檔案,繼續輸出大小和起始盤塊號
        if(unitTemp.type == 1)
        {
            int FCBBlock = unitTemp.startBlock;
            FCB* fileFCB = (FCB*)getBlockAddr(FCBBlock);
            printf("%d\t%d\t%d\n", fileFCB->fileSize, FCBBlock, fileFCB->blockNum);
        }else{
            int dirBlock = unitTemp.startBlock;
            dirTable* myTable = (dirTable*)getBlockAddr(dirBlock);
            printf("%d\t%d\n",myTable->dirUnitAmount, unitTemp.startBlock);
        }
    }
}
8)FileOperate.cpp

幾乎全部的內容都在上面做了闡述了

#include"FileOperate.h"

#include<string.h>
#include<stdio.h>



dirTable* rootDirTable; //根目錄
dirTable* currentDirTable;  //當前位置
char path[200]; //儲存當前絕對路徑


//初始化根目錄
void initRootDir()
{
    //分配一個盤塊空間給rootDirTable
    int startBlock = getBlock(1);
    if(startBlock == -1)
        return;
    rootDirTable = (dirTable*)getBlockAddr(startBlock);
    rootDirTable->dirUnitAmount = 0;
    //將自身作為父級目錄
    //addDirUnit(rootDirTable, "..", 0, startBlock);

    currentDirTable = rootDirTable;
    //初始化初始絕對路徑
    path[0]='\\';
    path[1]='\0';
}
//獲得絕對路徑
char* getPath()
{
    return path;
}
//展示當前目錄 ls
void showDir()
{
    int unitAmount = currentDirTable->dirUnitAmount;
    printf("total:%d\n", unitAmount);
    printf("name\ttype\tsize\tFCB\tdataStartBlock\n");
    //遍歷所有表項
    for(int i=0; i<unitAmount; i++)
    {
        //獲取目錄項
        dirUnit unitTemp = currentDirTable->dirs[i];
        printf("%s\t%d\t", unitTemp.fileName, unitTemp.type);
        //該表項是檔案,繼續輸出大小和起始盤塊號
        if(unitTemp.type == 1)
        {
            int FCBBlock = unitTemp.startBlock;
            FCB* fileFCB = (FCB*)getBlockAddr(FCBBlock);
            printf("%d\t%d\t%d\n", fileFCB->fileSize, FCBBlock, fileFCB->blockNum);
        }else{
            int dirBlock = unitTemp.startBlock;
            dirTable* myTable = (dirTable*)getBlockAddr(dirBlock);
            printf("%d\t%d\n",myTable->dirUnitAmount, unitTemp.startBlock);
        }
    }
}
//切換目錄 cd
int changeDir(char dirName[])
{
    //目錄項在目錄位置
    int unitIndex = findUnitInTable(currentDirTable, dirName);
    //不存在
    if(unitIndex == -1)
    {
        printf("file not found\n");
        return -1;
    }
    if(currentDirTable->dirs[unitIndex].type == 1)
    {
        printf("not a dir\n");
        return -1;
    }
    //修改當前目錄
    int dirBlock = currentDirTable->dirs[unitIndex].startBlock;
    currentDirTable = (dirTable*)getBlockAddr(dirBlock);
    //修改全域性絕對路徑
    if(strcmp(dirName, "..") == 0)
    {
        //回退絕對路徑
        int len = strlen(path);
        for(int i=len-2;i>=0;i--)
            if(path[i] == '\\')
            {
                path[i+1]='\0';
                break;
            }
    }else {
        strcat(path, dirName);
        strcat(path, "\\");
    }

    return 0;
}
//修改檔名或者目錄名 mv
int changeName(char oldName[], char newName[])
{
    int unitIndex = findUnitInTable(currentDirTable, oldName);
    if(unitIndex == -1)
    {
        printf("file not found\n");
        return -1;
    }
    strcpy(currentDirTable->dirs[unitIndex].fileName, newName);
    return 0;
}


//******************建立和刪除檔案
//建立檔案 touch
int creatFile(char fileName[], int fileSize)
{
    //檢測檔名字長度
    if(strlen(fileName) >= 59)
    {
        printf("file name too long\n");
        return -1;
    }
    //獲得FCB的空間
    int FCBBlock = getBlock(1);
    if(FCBBlock == -1)
        return -1;
    //獲取檔案資料空間
    int FileBlock = getBlock(fileSize);
    if(FileBlock == -1)
        return -1;
    //建立FCB
    if(creatFCB(FCBBlock, FileBlock, fileSize) == -1)
        return -1;
    //新增到目錄項
    if(addDirUnit(currentDirTable, fileName, 1, FCBBlock) == -1)
        return -1;

    return 0;
}
//建立目錄 mkdir
int creatDir(char dirName[])
{
    if(strlen(dirName) >= 59)
    {
        printf("file name too long\n");
        return -1;
    }
    //為目錄表分配空間
    int dirBlock = getBlock(1);
    if(dirBlock == -1)
        return -1;
    //將目錄作為目錄項新增到當前目錄
    if(addDirUnit(currentDirTable, dirName, 0, dirBlock) == -1)
        return -1;
    //為新建的目錄新增一個到父目錄的目錄項
    dirTable* newTable = (dirTable*)getBlockAddr(dirBlock);
    newTable->dirUnitAmount = 0;
    char parent[] = "..";
    if(addDirUnit(newTable, parent, 0, getAddrBlock((char*)currentDirTable)) == -1)
        return -1;
    return 0;
}
//建立FCB
int creatFCB(int fcbBlockNum, int fileBlockNum, int fileSize)
{
    //找到fcb的儲存位置
    FCB* currentFCB = (FCB*) getBlockAddr(fcbBlockNum);
    currentFCB->blockNum = fileBlockNum;//檔案資料起始位置
    currentFCB->fileSize = fileSize;//檔案大小
    currentFCB->link = 1;//檔案連結數
    currentFCB->dataSize = 0;//檔案已寫入資料長度
    currentFCB->readptr = 0;//檔案讀指標
    return 0;
}
//新增目錄項
int addDirUnit(dirTable* myDirTable, char fileName[], int type, int FCBBlockNum)
{
    //獲得目錄表
    int dirUnitAmount = myDirTable->dirUnitAmount;
    //檢測目錄表示是否已滿
    if(dirUnitAmount == dirTable_max_size)
    {
        printf("dirTables is full, try to delete some file\n");
        return -1;
    }

    //是否存在同名檔案
    if(findUnitInTable(myDirTable, fileName) != -1)
    {
        printf("file already exist\n");
           return -1;
    }
    //構建新目錄項
    dirUnit* newDirUnit = &myDirTable->dirs[dirUnitAmount];
    myDirTable->dirUnitAmount++;//當前目錄表的目錄項數量+1
    //設定新目錄項內容
    strcpy(newDirUnit->fileName, fileName);
    newDirUnit->type = type;
    newDirUnit->startBlock = FCBBlockNum;

    return 0;
}
//刪除檔案 rm
int deleteFile(char fileName[])
{
    //忽略系統的自動建立的父目錄
    if(strcmp(fileName, "..") == 0)
    {
        printf("can't delete ..\n");
        return -1;
    }
    //查詢檔案的目錄項位置
    int unitIndex = findUnitInTable(currentDirTable, fileName);
    if(unitIndex == -1)
    {
        printf("file not found\n");
        return -1;
    }
    dirUnit myUnit = currentDirTable->dirs[unitIndex];
    //判斷型別
    if(myUnit.type == 0)//目錄
    {
        printf("not a file\n");
        return -1;
    }
    int FCBBlock = myUnit.startBlock;
    //釋放記憶體
    releaseFile(FCBBlock);
    //從目錄表中剔除
    deleteDirUnit(currentDirTable, unitIndex);
    return 0;
}
//釋放檔案記憶體
int releaseFile(int FCBBlock)
{
    FCB* myFCB = (FCB*)getBlockAddr(FCBBlock);
    myFCB->link--;  //連結數減一
    //無連結,刪除檔案
    if(myFCB->link == 0)
    {
        //釋放檔案的資料空間
        releaseBlock(myFCB->blockNum, myFCB->fileSize);
    }
    //釋放FCB的空間
    releaseBlock(FCBBlock, 1);
    return 0;
}
//刪除目錄項
int deleteDirUnit(dirTable* myDirTable, int unitIndex)
{
    //遷移覆蓋
    int dirUnitAmount = myDirTable->dirUnitAmount;
    for(int i=unitIndex; i<dirUnitAmount-1; i++)
    {
        myDirTable->dirs[i] = myDirTable->dirs[i+1];
    }
    myDirTable->dirUnitAmount--;
    return 0;
}
//刪除目錄 rmdir
int deleteDir(char dirName[])
{
    //忽略系統的自動建立的父目錄
    if(strcmp(dirName, "..") == 0)
    {
        printf("can't delete ..\n");
        return -1;
    }
    //查詢檔案
    int unitIndex = findUnitInTable(currentDirTable, dirName);
    if(unitIndex == -1)
    {
        printf("file not found\n");
        return -1;
    }
    dirUnit myUnit = currentDirTable->dirs[unitIndex];
    //判斷型別
    if(myUnit.type == 0)//目錄
    {
        deleteFileInTable(currentDirTable, unitIndex);
    }else {
        printf("not a dir\n");
        return -1;
    }
    //從目錄表中剔除
    deleteDirUnit(currentDirTable, unitIndex);
    return 0;
}
//刪除檔案/目錄項
int deleteFileInTable(dirTable* myDirTable, int unitIndex)
{
   //查詢檔案
    dirUnit myUnit = myDirTable->dirs[unitIndex];
    //判斷型別
    if(myUnit.type == 0)//目錄
    {
        //找到目錄位置
        int FCBBlock = myUnit.startBlock;
        dirTable* table = (dirTable*)getBlockAddr(FCBBlock);
        //遞迴刪除目錄下的所有檔案
        printf("cycle delete dir %s\n", myUnit.fileName);
        int unitCount = table->dirUnitAmount;
        for(int i=1; i<unitCount; i++)//忽略“..”
        {
            printf("delete %s\n", table->dirs[i].fileName);
            deleteFileInTable(table, i);
        }
        //釋放目錄表空間
        releaseBlock(FCBBlock, 1);
    }else {//檔案
        //釋放檔案記憶體
        int FCBBlock = myUnit.startBlock;
        releaseFile(FCBBlock);
    }
    return 0;
}



//**********************讀寫操作
//讀檔案 read
int read(char fileName[], int length)
{
    int unitIndex = findUnitInTable(currentDirTable, fileName);
    if(unitIndex == -1)
    {
        printf("file no found\n");
        return -1;
    }
    //控制塊
    int FCBBlock = currentDirTable->dirs[unitIndex].startBlock;
    FCB* myFCB = (FCB*)getBlockAddr(FCBBlock);
    doRead(myFCB, length);
    return 0;
}
//重新讀檔案 reread
int reread(char fileName[], int length)
{
    int unitIndex = findUnitInTable(currentDirTable, fileName);
    if(unitIndex == -1)
    {
        printf("file no found\n");
        return -1;
    }
    //控制塊
    int FCBBlock = currentDirTable->dirs[unitIndex].startBlock;
    FCB* myFCB = (FCB*)getBlockAddr(FCBBlock);
    myFCB->readptr = 0;
    doRead(myFCB, length);

    return 0;
}
//執行讀操作
int doRead(FCB* myFCB, int length)
{
    //讀資料
    int dataSize = myFCB->dataSize;
    char* data = (char*)getBlockAddr(myFCB->blockNum);
    //在不超出資料長度下,讀取指定長度的資料
    for(int i=0; i<length && myFCB->readptr < dataSize; i++, myFCB->readptr++)
    {
        printf("%c", *(data+myFCB->readptr));
    }
    if(myFCB->readptr == dataSize)//讀到檔案末尾用#表示
        printf("#");
    //換行美觀
    printf("\n");
    return 0;
}
//寫檔案,從末尾寫入 write
int write(char fileName[], char content[])
{
    int unitIndex = findUnitInTable(currentDirTable, fileName);
    if(unitIndex == -1)
    {
        printf("file no found\n");
        return -1;
    }
    //控制塊
    int FCBBlock = currentDirTable->dirs[unitIndex].startBlock;
    FCB* myFCB = (FCB*)getBlockAddr(FCBBlock);
    doWrite(myFCB, content);
    return 0;
}
//重新寫覆蓋 rewrite
int rewrite(char fileName[], char content[])
{
    int unitIndex = findUnitInTable(currentDirTable, fileName);
    if(unitIndex == -1)
    {
        printf("file no found\n");
        return -1;
    }
    //控制塊
    int FCBBlock = currentDirTable->dirs[unitIndex].startBlock;
    FCB* myFCB = (FCB*)getBlockAddr(FCBBlock);
    //重設資料塊
    myFCB->dataSize = 0;
    myFCB->readptr = 0;

    doWrite(myFCB, content);
    return 0;
}
//執行寫操作
int doWrite(FCB* myFCB, char content[])
{
    int contentLen = strlen(content);
    int fileSize = myFCB->fileSize * block_szie;
    char* data = (char*)getBlockAddr(myFCB->blockNum);
    //在不超出檔案的大小的範圍內寫入
    for(int i=0; i<contentLen && myFCB->dataSize<fileSize; i++, myFCB->dataSize++)
    {
        *(data+myFCB->dataSize) = content[i];
    }
    if(myFCB->dataSize == fileSize)
        printf("file is full,can't write in\n");
    return 0;
}



//從目錄中查詢目錄專案
int findUnitInTable(dirTable* myDirTable, char unitName[])
{
    //獲得目錄表
    int dirUnitAmount = myDirTable->dirUnitAmount;
    int unitIndex = -1;
    for(int i=0; i<dirUnitAmount; i++)//查詢目錄項位置
       if(strcmp(unitName, myDirTable->dirs[i].fileName) == 0)
            unitIndex = i;
    return unitIndex;
}


工程程式碼連結(含有exe可直接執行):FileSystem