运用散列Hashtable模拟云盘(C++)

本文介绍了运用C++和散列Hashtable模拟云盘的实现,包括上传、下载、查看、删除文件等功能。通过散列实现秒传,详细探讨了实验原理、代码实现、存在的问题及实验心得。并提供了云盘测试指南。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、实验原理阐述

  本实验意在基于散列(Hash),运用C++编写exe文件(后称云盘.exe)实现云盘上传、下载、查看云盘内容、删除文件的功能。特别的,运用散列实现上传的秒传。根据实验目的,做出以下实验设计。
  一、云盘的结构。云盘主要有两部分,一部分是客户端,一部分是云端。客户端储存文件信息需要一个哈希表(hashtable,散列表,下称哈希表),客户端在使用时访问云端的哈希表,而客户端本身也是一种哈希表。因此本实验的客户端用哈希表模拟。云端用电脑中指定的一个文件夹模拟(本程序指定“D:\云盘”文件夹模拟,因此要求测试者在测试前在D盘创建一个名为“云盘”的文件夹。)。客户端用于操作,上传、下载、查看云盘文件内容、删除文件等,对象是许多个人。但是云端则由许多个人通过客户端传送的文件的汇总。一方面,当个人在客户端删除文件时,云端的文件不会受到影响。另一方面,每个人再上传文件的时候,根据云盘中的文件的hash值,区分上传的状态,“普通上传”或“秒传”。下面主要讨论云盘上传原理。
  二、云盘的上传。云盘的上传分为“普通上传”和“秒传”,如果在云端不存在同样内容的文件,则为“普通上传”,需要花费上传时间。如果在云端存在同样内容的文件,则为“秒传”,不需要花费传送时间。根据这个特点,结合散列表的性质,可以知道文件上传之后信息会储存在哈希表中,每个文件经过一个hash函数,得出一个对于这个内容独特的hash值。每个上传的文件的内容也会经过同样的hash函数计算求出它的hash值,并查看散列表对应hash值的空间是否已被占用,若被占用,说明云端上已存在可能文件名不一样而内容一样的文件,则无需再花费时间上传,故实现“秒传”,然后在客户端记录上传资料的信息。本实验用电脑的“复制文件”功能模拟打开云盘客户端上传文件到云端的操作。而C++自身代码是不能够完成对电脑的文件复制转移功能的,因此需要调用cmd。
  三、云盘的下载。本实验用将文件从“云端”(文件夹“D:\云盘”)复制要下载的文件到“下载目录”(由测试者指定的目录)来实现云盘的下载功能。同样需要调用cmd。
  四、查看云盘中的文件。在“云盘.exe”中输出上传的。
  五、删除云盘中的文件。原理上说,删除客户端中的文件是不会影响到云端的文件的,因此只是将要删除的指定文件夹的信息从客户端的哈希表中删除。
  根据以上实验设计,下面使用代码实现模拟云盘。

二、代码实现

1.首先构造哈希表的类:
#include<iostream>
#include<vector>
#include<fstream>
#include<algorithm>
#include<string>
#include<Windows.h>
#include<cstddef>
using namespace std;

template<typename HashedObj>
class HashTable
{
public:
    HashTable( int size = 101 ) 
    {
        currentSize = size;
        vector< vector<HashedObj> > theLists( currentSize );
        this -> theLists = theLists;
    };//构造函数

    bool contains( const HashedObj & x ) const;
    void makeEmpty();
    bool insert( HashedObj x, HashedObj path );
    bool insert( HashedObj x );
    int getsize();
    bool remove( const HashedObj & x );
    bool isEmpty( const HashedObj & x );

    void loadcloud();
    void upload();
    void download();
    void viewfile();
    void exit();
    void deletefile();

private:
    vector< vector < HashedObj > > theLists;
    int currentSize;
    int hash( HashedObj  x ) ;
};
2.各函数的实现:
  • 判断是否包含内容 ( contains函数 ):

    包含则返回1,不包含则返回0;

template <typename HashedObj> bool HashTable<HashedObj>::contains(const HashedObj &x) const
{
    const vector<HashedObj> &whichList = theLists[myhash(x,theLists.size())];
    return find( whichList.begin(), whichList.end(), x ) != whichList.end();
}
  • 清空所有链表 ( makeEmpty函数 ):
template <typename HashedObj> void HashTable<HashedObj>::makeEmpty()
{
    int size0 = theLists.size();
    for(int i=0;i<size0;i++)
    {
        theLists[i].clear();
    }
    cout<<"已清空云盘"<<endl;
}
  • 插入内容 ( insert函数 ):
     判断要插入的内容hash值是否冲突:
     若冲突,则返回0;
     若不冲突,则在hash值对应的向量后面第一格插入内容,第二个插入文件路径,并且返回1。
template <typename HashedObj> bool HashTable<HashedObj>::insert( HashedObj x, HashedObj path )
{
    vector< HashedObj > & whichList = theLists[ hash( x ) ];
    if( find( whichList.begin(), whichList.end(), x ) != whichList.end() ) {
        //cout<<"冲突"<<endl;
        return false;
    }
    whichList.push_back( x );
    size_t found = path.find_last_of("/\\");
    //cout << path.substr( found + 1 )<<endl;
    whichList.push_back( path.substr( found + 1 ) );
    return true;
}
  • 获得散列大小 ( getsize函数 ):
template <typename HashedObj> int HashTable<HashedObj>::getsize()
{
    return theLists.size();
}//获得散列大小
  • 移除一个元素 ( remove函数 ):
     判断元素是否移除:
     用find函数遍历哈希表,如果没有要删除的内容,返回0;
     如果找到要删除的内容,则调用vector类的erase函数删除该处的内容,并返回1;
template <typename HashedObj> bool HashTable<HashedObj>::remove( const HashedObj & x )
{
    vector<HashedObj> & whichList = theLists[ hash( x ) ];
    vector<HashedObj>::iterator itr = find( whichList.begin(), whichList.end(), x );
    if( itr == whichList.end() )
        return false;
    whichList.erase( itr );
    --currentSize;
    return true;
}
  • 哈希函数 ( hash函数 ):
     计算输入的内容的hash值。
template <typename HashedObj> int HashTable<HashedObj>::hash(  HashedObj  x ) 
{
    int a,b,c;
    a = x[ x.size() - 1 ];
    b = x[ 0 ];
    c = x[ ( x.size() - 1 ) / 2 ];
    return ( a + b + c ) % 99;//意味着只有99格可以存放
}
  • 上传文件 ( upload函数 ):

  输入需要上传的文件名(包含地址,形如:D:\新建文件夹\1.txt)。
  1.如果输入0,则返回输入上传操作指令步骤;
  2.如果输入不存在的文件名,返回“该文件不存在”;
  3.如果输入正确的文件则读取文件内容,计算hash值并判断是否冲突。若不冲突,则存入哈希表,上传(调用cmd复制文件)到位于D盘的“云端”文件夹,并输出“状态:正常上传”;若冲突,则只输入“状态:秒传”。

template <typename HashedObj> void HashTable<HashedObj>::upload() 
{
    string str;
    cout << "请输入需要上传的文件:(输入0返回操作选择)" << endl;
    cin >> str;
    if( int(str[0]) != 48 ) {
        string b;
        if( isfileexist( str ) ) {
            b = loadfile( str );
            //cout<<"^"<<b<<endl;
            //cout<<hash( b )<<endl;
            cout << "文件 " << str << " 已上传" << endl;
            if( insert( b, str ) )  {
                cout << "状态: 普通上传" << endl;
                //复制到云端的文件夹
                string cmd1 = "xcopy ";
                string cmd2 = " D:\\云盘";
                string cmd = cmd1+str+cmd2;
                //cout<<cmd<<endl;
                //int cmdsize = cmd.size();
                const char*c = cmd.c_str();
                system( c );
            }
            else 
                cout << "状态: 秒传" << endl;
        }
        else 
            cout<<"该文件不存在"<<endl;
    }
    else ;
};
  • 下载文件 ( download函数 ):

  输入需要下载的文件名(包含地址,形如:D:\新建文件夹\1.txt)。
  输入下载目录(形如:D:\)。
  把所选文件从云端下载(调用cmd复制)到下载目录中。

template <typename HashedObj> void HashTable<HashedObj>::download() 
{
    cout<<"请输入要下载的文件名称:(输入0返回操作选择)"<<endl;
    string dlocation = "D:\\云盘\\";
    string str;
    cin >> str;

    if( int(str[0]) != 48 ) {
        cout<<"请输入下载目录:"<<endl;
        string location;
        cin >> location;
        //cout << dlocation+str <<endl;
        string b;
        b = loadfile( dlocation + str );
        string cmd1="xcopy ";
        string space=" ";
        //cout<<cmd1+dlocation+str+space+location<<endl;
        string cmd = cmd1 + dlocation + str + space + location;
        //cout<<cmd<<endl;

        int cmdsize = cmd.size();
        const char*c = cmd.c_str();
        system(c);
        //把云端的指定文件复制到指定地址
    }
    else ;
}
  • 查看云盘内容 ( viewfile函数 ):

  如果云盘中无文件,则输出“该云盘是空的”。
  如果云盘中有文件,则逐个输出文件名(形如1.txt)。

template <typename HashedObj> void HashTable<HashedObj>::viewfile() 
{
    int k = 0;
    string fname;
    for( int i = 0; i <= 99; i++ ) {
        vector< HashedObj > & whichList = theLists[ i ];
        int ListSize = whichList.size();
        if( ListSize > 0 ) {
            //cout<< whichList[1] <<endl;
            fname = whichList[1];
            splitfilename( fname );
            k = 1;
        }
        else ;
    }
    if( k == 0 )
        cout << "该云盘是空的" << endl;
}
  • 退出云盘 ( exit函数 ):
      只输出”退出”。(在main函数中写入break跳出输入云盘命令循环。)
template <typename HashedObj> void HashTable<HashedObj>::exit() 
{
    cout<<"退出"<<endl;
};
  • 删除文件 ( deletefile函数 ):
      输入要删除的文件的文件名( 形如1.txt ),若文件正常删除,则输出“文件已删除”,否则输出“文件未删除”。
template <typename HashedObj> void HashTable<HashedObj>::deletefile() 
{
    string del = "del ";
    string dlocation = "D:\\云盘\\";
    cout<<"请输入需要删除的文件:(输入0返回操作选择)"<<endl;
    string str;
    cin>>str;

    if( int(str[0]) != 48 ) {
        string cmd = del + dlocation + str;
        int cmdsize = cmd.size();
        const char*c = cmd.c_str();

        string a = loadfile( dlocation + str );
        if( remove( a ) )
            cout<<"文件已删除"<<endl;
        else 
            cout<<"文件未删除"<<endl;
        system(c);
    }
    else ;
}
  • 读取文件 ( loadfile函数 ):
      读取在path路径下的txt文件内容,以string形式返回。(只能返回txt中第一行第一个空格前或第一行的内容。)
string loadfile( string path ) 
{
    string a;
    ifstream f1;
    f1.open( path );
    f1 >> a;
    f1.close();
    return a;
}
  • 分离文件名称 ( splitfilename函数 ):
      将一个路径中的文件名称分离出来。(例如:D:\新建文件夹\1.txt,用splitfilename函数作用后输出“1.txt”。
void splitfilename ( const string & str ) 
{
    size_t found = str.find_last_of("/\\");
    cout << str.substr( found + 1 )<<endl;
}
  • 加载云盘 ( loadcloud函数 ):
      每次启动云盘.exe时加载云盘,读取“云盘”文件夹中所有文件并插入到哈希表中。
template <typename HashedObj> void HashTable<HashedObj>::loadcloud() 
{
    string DIR = "dir D:\\云盘/on/b>D:\\list.txt";//在D盘的位置生成云端文件夹内容清单
    const char *dir = DIR.c_str();
    system(dir);

    char str[100];
    string txtpath = "D:\\list.txt";
    const char *tpath = txtpath.c_str();
    ifstream f2;
    f2.open( tpath );
    int i = 0;
    while(f2.getline(str,sizeof(str))) {
        string dlocation = "D:\\云盘\\";
        string sum = dlocation + str;
        //const char *d = sum.c_str();
        string s;
        s = loadfile( sum );
        insert( s, str );
    }
    f2.close();
}
  • 判断文件是否存在 ( isfileexist函数 ):
      对于输入的路径path,判断是否能够读取该路径文件。若成功读取,返回1,否则返回0。
bool isfileexist( string path ) 
{
    const char* j = path.c_str();
    ifstream fin( j );
    if(!fin) return 0;
    else return 1;
}
  • 主函数
     打开云盘.exe界面的显示内容、操作指引的设计。在“请选择功能(输入下面功能的编号)”下:若输入1,则执行“上传”操作;输入2,则执行“查看云盘内容”操作;输入3,则执行“下载”操作;输入4,则执行“退出”操作,跳出循环;输入5,则执行“删除文件”操作;其余输入显示“非法命令”。(标注隐藏项将在实验缺陷处提及。)
int main() {
    HashTable<string> H(100);
    H.loadcloud();//加载云盘
    cout<<endl;
    cout<<"———这是一个云盘客户端———"<<endl<<endl;
    cout<<" made by Yang"<<endl;
    cout<<"____________________________________"<<endl<<endl;
    cout<<"请选择功能(输入下面功能的编号)"<<endl;
    cout<<"1 上传"<<endl;
    cout<<"2 查看云盘内容"<<endl;
    cout<<"3 下载"<<endl;
    cout<<"4 退出"<<endl;
    cout<<"5 删除文件"<<endl;
    cout<<endl;
    cout<<"请输入功能的编号"<<endl;
    string n;
    while( cin >> n ) { 
        if( n[0] == 49 ) 
            H.upload();
        else 
            if( n[0] == 50 ) 
                H.viewfile();
            else
                if( n[0] == 51 ) 
                    H.download();
                else 
                    if( n[0] == 52 ) {
                        H.exit();
                        break;
                    }
                    else 
                        if( n[0] == 53 ) 
                            H.deletefile();
                        else 
                            if( n[0] == 54 )
                                H.makeEmpty();//隐藏
                            else 
                                cout<<"非法命令"<<endl;
        cout<<endl;
        cout<<"请输入功能的编号"<<endl;
        H.loadcloud();
    }
    return 0;
}

三、实验缺陷

  在探索实验过程中,发现本模拟实验存在很多缺陷。其中有:
  1.没有很好的区分出云盘和客户端。原因是客户端和云端使用同一哈希表。实验最初为了实现云端的文件在运行“云盘.exe”时自动将文件夹中信息存入哈希表,这可以实现上传的秒传功能,而删除客户端哈希表信息的时候必须同云端的文件一同删除。而代码中”隐藏”项则是只清空散列表而不对云盘文件夹做出任何改变,此处为一大bug。
  2.面向对象单一,不能多人使用。
  3.当与云端有内容一样但文件名不一样的文件上传时,不会将文件的文件信息存入哈希表中。
  4.上传的文件只能是txt文件。
  5.hash函数不严谨,可能产生不同的文件也有冲突的情况。

四、实验心得体会

  本次实验收获挺多,为了实现云盘去查阅了很多的资料,对散列的理解更加深入,尤其是对云盘的原理也有了更多的了解,开拓了视野,而且发现了乐趣。而且借此机会学习了用C++调用cmd的方法,也意识到string类的便捷性。虽然还存在很多bug,但是也尽力去完成了。


云盘测试指南

一、在D盘目录下新建文件夹并命名为“云盘”。

二、运行“云盘.exe”,看到页面:

搜狗截图20160512185242.jpg-36kB

三、在光标处输入功能的编号

若需要上传则输入1;
若需要查看云盘内容则输入2;
若需要下载文件则输入3;
若需要退出云盘则输入4;
若需要删除文件则输入5;

四、各功能指引:

1.上传文件
在光标处输入1,在“请输入需要上传的文件中”输入文件名称,格式为“D:\1.txt”,输入完毕之后回车。
可见输出:
搜狗截图20160512192815.jpg-48.6kB
普通上传则说明在云端不存在该文件,秒传则说明云端已存在该文件。

2.查看云盘内容
在“请输入功能的编号”下光标处输入2,可见输出。
该云盘是空的则输出:
搜狗截图20160512193143.jpg-12.7kB
若非空,则输出:
搜狗截图20160512193046.jpg-6.1kB

3.下载文件
在光标处输入3,在“请输入需要上传的文件中”输入文件名,格式为:1.txt,输入完毕之后回车。再在“请输入下载目录”处输入下载目录,格式为“D:\新建文件夹”,输入完毕之后回车。
可见输出:
1.jpg-28.9kB

4.删除文件
在光标处输入4,在“请输入需要上传的文件中”输入文件名,格式为:1.txt,输入完毕之后回车。
可见输出:
2.jpg-19.5kB

5.退出
在光标处输入5,回车。输出“退出”并点击任意键即可退出。

注:上传、下载和删除文件操作时可以输入0退出该操作并重新输入操作指令。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值