zoukankan      html  css  js  c++  java
  • c++实现文件复制并修改相应属性

    问题描述

    完成一个目录复制命令mycp,包括目录下的文件和子目录, 运行结果如下:

    beta@bugs.com [~/]# ls –la sem
    
    total 56
    
    drwxr-xr-x  3 beta beta 4096 Dec 19 02:53 ./
    
    drwxr-xr-x  8 beta beta 4096 Nov 27 08:49 ../
    
    -rw-r--r--  1 beta beta  128 Nov 27 09:31 Makefile
    
    -rwxr-xr-x  1 beta beta 5705 Nov 27 08:50 consumer*
    
    -rw-r--r--  1 beta beta  349 Nov 27 09:30 consumer.c
    
    drwxr-xr-x  2 beta beta 4096 Dec 19 02:53 subdir/
    
    beta@bugs.com [~/]# mycp sem target
    
    beta@bugs.com [~/]# ls –la target
    
    total 56
    
    drwxr-xr-x  3 beta beta 4096 Dec 19 02:53 ./
    
    drwxr-xr-x  8 beta beta 4096 Nov 27 08:49 ../
    
    -rw-r--r--  1 beta beta  128 Nov 27 09:31 Makefile
    
    -rwxr-xr-x  1 beta beta 5705 Nov 27 08:50 consumer*
    
    -rw-r--r--  1 beta beta  349 Nov 27 09:30 consumer.c
    
    drwxr-xr-x  2 beta beta 4096 Dec 19 02:53 subdir/
    

    思路

    这道题目主要涉及文件读写操作和属性修改。需要支持文件夹复制、文件复制,在Linux下还要支持软链接的复制。

    思路如下:

    • 获取待复制目录的绝对路径
    • 根据绝对路径进行dfs或者bfs搜索所有子目录项
    • 判断子目录是属于什么类型:文件夹、普通文件、软链接
    • 分别对三种(Windows下只有文件夹和文件)进行复制操作
    • 修改目标文件属性与源文件保持一致

    使用到的函数主要有:

    Linux

    判断文件类型

    1. int lstat(const char *pathname, struct stat *statbuf);

      • const char *pathname 需要判断的文件的路径
      • struct stat *statbuf 用于保存文件属性
      • return int 0成功,-1失败
    2. 判断

      文件类型 说明 判断函数 例子
      普通文件 一般意义上的文件 S_ISREG() hello.c
      目录文件 可包含其他文件或者目录 S_ISDIR() /etc/
      字符设备文件 以无缓冲方式,提供对于设备的可变长度访问 S_ISCHR() /dev/tty
      块设备文件 以带缓冲方式,提供对于设备的固定长度访问 S_ISBLK() /dev/sda
      符号链接文件 指向另一个文件 S_ISLNK() /dev/cdrom
      命名管道文件 用于进程间通信 S_ISFIFO() /dev/inictl
      网络套接字文件 用于进程间的网络通信 S_ISSOCK() /dev/log

      值得注意的是,需要先判断是不是符号链接文件。对符号链接文件进行S_ISREG()判断时会出现将其判断为普通文件的情况。可能是由于他判断的是链接文件所指向的文件的类型。因此需要先判断是不是链接文件。

    遍历文件目录

    1. DIR *opendir(const char *name);

      • const char *name 待打开的目录路径
      • return DIR 返回目录数据结构
    2. struct dirent *readdir(DIR *dirp);

      • ``````DIR *dirp``` 待读取的目录

      • return struct dirent* 返回顺序读取的该目录下的目录项

        注意,第一个目录项是., 第二个目录项是..

    复制文件

    1. int open(const char *pathname, int flags, mode_t mode);
      • const char* pathname 待打开的文件路径
      • int flags O_RDONLY, O_WRONLY, or O_RDWR
      • mode_t mode
      • return int 返回值是打开的文件描述符
    2. int creat(const char *pathname, mode_t mode);
      • const char* pathname 待创建的文件名(可以包含路径)
      • mode_t mode 创建属性
      • return int 待创建文件描述符
    3. ssize_t read(int fd, void *buf, size_t count);
      • int fd 待读取的文件的描述符
      • void* buf 读取的内容存放缓冲区。尽管这里是void*buf在创建的时候应该是```char*``````
      • ​ ``````size_t count 要读取的内容的大小。如果大于fd```中的数目,则读取相应大小内容
      • return ssize_t 返回实际读取的内容的数目。失败返回-1
    4. ssize_t write(int fd, const void *buf, size_t count);
      • int fd 要写入的文件的描述符
      • const void *buf 要写入的内容。
      • size_t count 要写入的内容大小
      • return ssize_t 成功返回实际写入的数目。失败返回-1

    复制文件夹

    复制文件夹就是创建同名文件夹

    1. int mkdir(const char *pathname, mode_t mode);
      • const char* pathname 待创建的目录项的地址
      • mode_t mode 创建目录项的属性
      • return int 如果成功返回为0,否则返回-1

    复制软链接

    1. ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);
      • const cha* pathname 待读取软链接的路径
      • char* buf 软链接中的内容保存到buf
      • size_t bufsiz buf缓冲区的大小
      • return ssize_t 返回值是软链接指向路径的长度
    2. int symlink(const char *target, const char *linkpath);
      • const char* target 待创建的软链接的路径
      • const char* linkpath 待创建的软链接所指向的路径
      • return int 成功返回0,否则返回-1

    获取属性

    1. int lstat(const char *pathname, struct stat *statbuf);

      • const char *pathname 需要提取属性的文件或者文件夹的路径
      • struct stat *statbuf 获取到的属性存储缓冲区
      • return int 成功返回0,否则-1
    2. int chmod(const char *pathname, mode_t mode);

      • const char *pathname 待修改属性的文件的路径
      • mode_t mode 将待修改文件修改改为该属性
      • return int 若成功返回0,否则返回-1
    3. int chown(const char *pathname, uid_t owner, gid_t group);

      • const char* pathname 待更改的目录路径
      • uid_t owner 如果是-1则不改变属性
      • gid_t group 如果是-1则不改变属性
    4. int lutimes(const char *filename, const struct timeval tv[2]);

      这个命令用于修改目标文件的access_time modify_timelutimes可以修改软链接的属性。

      • const char *filename 待修改的文件路径
      • const struct timeval tv[2] tv[0]access_timetv[1]modify_time
      • return int 如果成功返回0,否则返回-1

    Windows

    这里主要列出所用到的函数,有需要的话可以详细去MSDN查相关的函数说明:

    FindFirstFile

    FindNextFile

    CreateFile

    GetFileSize

    ReadFile

    WriteFile

    _wmkdir

    GetFileAttributes

    SetFileAttributes

    GetFileTime

    SetFileTime

    源代码实现

    Linux

    #include <iostream>
    #include <string>
    #include <unistd.h>
    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <dirent.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <utime.h>
    #include <time.h>
    #include <sys/time.h>
    
    //文件夹复制采用dfs或者bfs进行搜索是等价的
    void searchDfs(char *src_path, char *dest_path);
    
    //复制文件
    void copyFile(const char *src_file, const char *dst_file);
    
    //复制文件夹
    void copyDir(const char *src_dir, const char *dst_dir);
    
    //复制软链接
    void copySln(const char *src_file, const char *dst_file);
    
    //修改文件属性与源文件保持一致
    void changeAttr(const char *src, const char *dst);
    
    //在src路径后面加上cat路径
    void changePathname(char *src, char *cat)
    {
        //在src路径后面加上cat路径
        strcat(src, (char *)"/");
        strcat(src, cat);
    }
    
    int main(int argc, char const *argv[])
    {
        //异常处理
        if (argc < 3)
        {
            printf("No file or directory specified
    ");
            exit(-1);
        }
        if (argc > 3)
        {
            printf("Too many arguments
    ");
            exit(-1);
        }
    
        char src_path[1024], dest_path[1024];
        char *current_dir = getcwd(NULL, 0); //获得当前路径
    
        //判断需要复制的是文件还是文件夹
        struct stat state_of_entry;
        lstat(argv[1], &state_of_entry);
        if (S_ISDIR(state_of_entry.st_mode)) //如果要复制的是文件夹
        {
            if (chdir(argv[1])) //目录错误
            {
                perror("chdir");
                exit(-1);
            }
            strcpy(src_path, getcwd(NULL, 0)); //获取源文件夹绝对路径
            chdir(current_dir);
    
            //判断第二个参数是不是目标路径
            lstat(argv[2], &state_of_entry);
            if (S_ISDIR(state_of_entry.st_mode)) //目录
            {
                if (chdir(argv[2])) //目录错误
                {
                    perror("chdir");
                    exit(-1);
                }
                strcpy(dest_path, getcwd(NULL, 0)); //获取目标文件夹绝对路径
                chdir(current_dir);
    
                chdir(dest_path);
    
                chdir(src_path); //切换到要复制的文件夹
                searchDfs(src_path, dest_path);
            }
            //如果目标路径错误
            else
            {
                printf("error. No destination directory.
    ");
                exit(-1);
            }
        }
    
        else //文件直接复制
        {
            char dest_path[1024];
            lstat(argv[2], &state_of_entry);
            if (S_ISDIR(state_of_entry.st_mode)) //目录
            {
                strcpy(dest_path, getcwd(NULL, 0)); //获取目标文件夹绝对路径
            }
            else
            {
                strcpy(dest_path, "./");
                strcat(dest_path, argv[2]);
            }
            copyFile(argv[1], argv[2]);
        }
    
        return 0;
    }
    
    void searchDfs(char *src_path, char *dest_path)
    {
        DIR *src_dir = opendir(src_path);
        DIR *dest_dir = opendir(dest_path);
        struct dirent *entry = NULL;
        struct stat state_of_entry;
        while ((entry = readdir(src_dir)) != NULL)
        {
            if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
                continue;
    
            lstat(entry->d_name, &state_of_entry);
            if (S_ISLNK(state_of_entry.st_mode)) //符号链接
            {
                char src_file[1024];
                char dest_file[1024];
                strcpy(src_file, src_path);
                //获取源文件绝对路径
                changePathname(src_file, entry->d_name);
                strcpy(dest_file, dest_path);
                //获取目标文件绝对路径
                changePathname(dest_file, entry->d_name);
                //复制
                copySln(src_file, dest_file);
            }
            else if (S_ISREG(state_of_entry.st_mode)) //普通文件
            {
                char src_file[1024];
                char dest_file[1024];
                strcpy(src_file, src_path);
                changePathname(src_file, entry->d_name);
                strcpy(dest_file, dest_path);
                changePathname(dest_file, entry->d_name);
                copyFile(src_file, dest_file);
            }
            else if (S_ISDIR(state_of_entry.st_mode)) //目录
            {
                char src[1024];
                char dest[1024];
                strcpy(src, src_path);
                changePathname(src, entry->d_name);
                strcpy(dest, dest_path);
                changePathname(dest, entry->d_name);
                copyDir(src, dest);
                searchDfs(src, dest);
            }
        }
        //保持目标文件夹与源文件夹属性相同
        changeAttr(src_path, dest_path);
    }
    
    void copyFile(const char *src_file, const char *dest_file)
    {
        int src_fd = open(src_file, O_RDONLY);
        int dest_fd = creat(dest_file, O_WRONLY);
    
        unsigned char buf[1024];
        while (read(src_fd, buf, sizeof(buf)) > 0)
        {
            write(dest_fd, buf, sizeof(buf));
        }
    
        changeAttr(src_file, dest_file);
    
        close(src_fd);
        close(dest_fd);
    }
    
    void copyDir(const char *src_dir, const char *dst_dir)
    {
        mkdir(dst_dir, 0777);
        changeAttr(src_dir, dst_dir);
    }
    
    void copySln(const char *src_file, const char *dst_file)
    {
        //软链接所指向的文件路径
        char slink_path[1024];
        memset(slink_path, 0, sizeof(slink_path));
        int len = 0;
        if ((len = readlink(src_file, slink_path, sizeof(slink_path))) > 0)
        {
            char src_dir[1024];
            strcpy(src_dir, src_file);
            //获取源文件的绝对路径,不含文件名
            for (int i = strlen(src_dir); i > -1; i--)
            {
                if (src_dir[i] == '/')
                {
                    src_dir[i + 1] = 0;
                    break;
                }
            }
    
            if (slink_path[0] != '/')        //一定是相对路径,如果首字符是'/'则是从根目录出发的绝对路径
                strcat(src_dir, slink_path); //将相对软链接的路径添加到软链接文件路径的后面
            else
                strcpy(src_dir, slink_path); //绝对路径拷贝过去即可
    
            //保存软链接所指向的文件的名字
            char file_name[1024];
            //提取所指向的文件的目录,由于可能是相对路径,顾切换到对应的文件夹下获取绝对路径
            for (int i = strlen(src_dir); i > -1; i--)
            {
                if (src_dir[i] == '/')
                {
                    strcpy(file_name, src_dir + i);//将软链接所指向的文件名保存在file_name中
                    src_dir[i + 1] = 0;
                    break;
                }
            }
    
            chdir(src_dir);//切换到软链接所指向的文件的路径
            strcpy(src_dir, getcwd(NULL, 0)); //获取软链接所指向文件的绝对路径
    
            strcat(src_dir, file_name);//添加软链接所指向的文件名即组成了绝对路径
    
            //创建软链接
            if (symlink(src_dir, dst_file) == -1)
            {
                perror("symlink");
            }
    
            //修改目标软链接属性使其与源软链接属性一致
            changeAttr(src_file, dst_file);
        }
        else
        {
            printf("%s: 软链接错误
    ",src_file);
        }
        
    }
    
    void changeAttr(const char *src, const char *dst)
    {
        struct stat attr_of_src;
        lstat(src, &attr_of_src);
    
        //修改文件属性
        chmod(dst, attr_of_src.st_mode);
        //修改文件用户组
        chown(dst, attr_of_src.st_uid, attr_of_src.st_gid);
    
        //修改文件访问、修改时间
        if (S_ISLNK(attr_of_src.st_mode))
        {
            struct timeval time_buf[2];
            time_buf[0].tv_sec = attr_of_src.st_atim.tv_sec;
            time_buf[0].tv_usec = attr_of_src.st_atim.tv_nsec / 1000;
            time_buf[1].tv_sec = attr_of_src.st_mtim.tv_sec;
            time_buf[1].tv_usec = attr_of_src.st_mtim.tv_nsec / 1000;
            if (lutimes(dst, time_buf) == -1)
            {
                printf("%s
    ", dst);
                perror("lutimes");
            }
        }
        else
        {
            struct utimbuf tbuf;
            tbuf.actime = attr_of_src.st_atime;
            tbuf.modtime = attr_of_src.st_mtime;
            utime(dst, &tbuf);
        }
    }
    

    Windows

    #define _CRT_SECURE_NO_WARNINGS
    #include <iostream>
    #include <Windows.h>
    #include <cstring>
    #define BUF_SIZE 1024
    using namespace std;
    
    //文件夹复制采用dfs或者bfs进行搜索是等价的
    void searchDfs(wchar_t* src_path, wchar_t* dest_path);
    //复制文件
    void copyFile(const wchar_t* src_file, const wchar_t* dst_file);
    //复制文件夹
    void copyDir(const wchar_t* src_dir, const wchar_t* dst_dir);
    //修改文件属性与源文件保持一致
    void changeAttr(const wchar_t* src_name, const wchar_t* dst_name, HANDLE h_src, HANDLE h_dst);
    
    int wmain(int argc, wchar_t* argv[])
    {
    	setlocale(LC_ALL, "");//设置wchar支持中文
    	wchar_t* src_path = new wchar_t[BUF_SIZE];
    	wchar_t* dst_path = new wchar_t[BUF_SIZE];
    	ZeroMemory(src_path, BUF_SIZE);
    	ZeroMemory(dst_path, BUF_SIZE);
    	wcscpy(src_path, argv[1]);//获取源文件夹路径
    	wcscat(src_path, L"\*");//末尾添加"\*"表明要对其进行遍历
    	wcscpy(dst_path, argv[2]);//获取目标文件夹路径
    	searchDfs(src_path, dst_path);//对源文件夹进行搜索遍历复制
    	delete[] src_path;
    	delete[] dst_path;
    	return 0;
    }
    
    void searchDfs(wchar_t* src_path, wchar_t* dst_path)
    {
    	//保存文件夹下的目录信息
    	WIN32_FIND_DATA find_data;
    	//第一个文件的handle
    	HANDLE h_find = FindFirstFile(src_path, &find_data);
    	if (h_find == INVALID_HANDLE_VALUE)
    	{
    		cout << "Fail to find first file" << endl;
    		return;
    	}
    
    	copyDir(src_path, dst_path);//目标文件夹不存在时先创建
    
    	//遍历该文件夹下的所有文件和文件夹
    	do
    	{
    		//wcout << find_data.cFileName << endl;
    		if (wcscmp(find_data.cFileName, L".") == 0 || wcscmp(find_data.cFileName, L"..") == 0)
    		{
    			continue;
    		}
    		//该项是目录
    		if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    		{
    			wchar_t n_src_path[BUF_SIZE];
    			wcscpy(n_src_path, src_path);
    			n_src_path[wcslen(n_src_path) - 1] = '';//删除最后一位的'*'
    			wcscat(n_src_path, find_data.cFileName);//添加下层文件夹名称以获取下级目录
    			wcscat(n_src_path, L"\*");
    
    			wchar_t n_dst_path[BUF_SIZE];
    			wcscpy(n_dst_path, dst_path);
    			wcscat(n_dst_path, L"\");
    			wcscat(n_dst_path, find_data.cFileName);//要复制到的目标文件夹路径
    
    			copyDir(n_src_path, n_dst_path);//复制文件夹
    
    			searchDfs(n_src_path, n_dst_path);//向下搜索
    		}
    		else//该项是文件
    		{
    			//获得该项的绝对路径
    			wchar_t n_src_path[BUF_SIZE];
    			wcscpy(n_src_path, src_path);
    			n_src_path[wcslen(n_src_path) - 1] = '';//删除最后一位的'*'
    			wcscat(n_src_path, find_data.cFileName);//添加文件名称以获取绝对路径
    			
    			//要复制到的文件的绝对路径
    			wchar_t n_dst_path[BUF_SIZE];
    			wcscpy(n_dst_path, dst_path);
    			wcscat(n_dst_path, L"\");
    			wcscat(n_dst_path, find_data.cFileName);
    
    			copyFile(n_src_path, n_dst_path);
    		}
    
    	} while (FindNextFile(h_find, &find_data));
    
    	//目标文件夹已存在,这里实际上只是修改目的权限与源权限一致
    	copyDir(src_path, dst_path);
    
    	return;
    }
    
    
    //复制文件
    void copyFile(const wchar_t* src_file, const wchar_t* dst_file)
    {
    	//打开源文件
    	HANDLE h_src = ::CreateFile(
    			src_file, 
    			GENERIC_READ, 
    			0, 
    			NULL, 
    			OPEN_EXISTING,
    		    FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, 
    			NULL);
    	//创建目标文件
    	HANDLE h_dst = ::CreateFile(
    			dst_file,
    			GENERIC_WRITE | GENERIC_READ,
    			0,
    			NULL,
    			CREATE_ALWAYS,
    			FILE_ATTRIBUTE_NORMAL,
    			NULL);
    
    	if (h_src == INVALID_HANDLE_VALUE || h_dst == INVALID_HANDLE_VALUE)
    	{
    		printf("Open file error!
    ");
    		CloseHandle(h_src);
    		CloseHandle(h_dst);
    		exit(-1);
    	}
    
    	DWORD all_bytes = GetFileSize(h_src, NULL);
    	char* buffer = new char[all_bytes + 1];
    	ZeroMemory(buffer, all_bytes + 1);
    
    	//复制文件
    	DWORD bytes = 0;
    	ReadFile(h_src, buffer, all_bytes, &bytes, NULL);
    	WriteFile(h_dst, buffer, all_bytes, &bytes, NULL);
    
    	//修改文件属性
    	changeAttr(src_file, dst_file, h_src, h_dst);
    
    	CloseHandle(h_src);
    	CloseHandle(h_dst);
    	delete[] buffer;
    	return;
    }
    
    //复制文件夹
    void copyDir(const wchar_t* src_dir_name, const wchar_t* dst_dir)
    {
    	//获得源目录路径
    	wchar_t* src_dir = new wchar_t[BUF_SIZE];
    	wcscpy(src_dir, src_dir_name);
    	src_dir[wcslen(src_dir) - 2] = '';
    
    	//创建文件夹
    	_wmkdir(dst_dir);
    
    	//打开源文件夹
    	HANDLE h_src = CreateFile(
    		src_dir,
    		GENERIC_READ,
    		FILE_SHARE_READ | FILE_SHARE_DELETE,
    		NULL,
    		OPEN_EXISTING,
    		FILE_FLAG_BACKUP_SEMANTICS,
    		NULL
    	);
    	if (h_src == INVALID_HANDLE_VALUE)
    	{
    		printf("Open src directory error!
    ");
    		CloseHandle(h_src);
    		exit(-1);
    	}
    
    	//打开目标文件夹
    	HANDLE h_dst = CreateFile(
    		dst_dir,
    		GENERIC_READ | GENERIC_WRITE,
    		FILE_SHARE_READ | FILE_SHARE_DELETE,
    		NULL,
    		OPEN_EXISTING,
    		FILE_FLAG_BACKUP_SEMANTICS,
    		NULL
    	);
    	if (h_dst == INVALID_HANDLE_VALUE)
    	{
    		printf("Open dst directory error!
    ");
    		CloseHandle(h_dst);
    		exit(-1);
    	}
    	//修改目标文件夹属性与源文件夹属性一致
    	changeAttr(src_dir, dst_dir, h_src, h_dst);
    
    	CloseHandle(h_src);
    	CloseHandle(h_dst);
    	return;
    }
    
    
    //修改文件属性与源文件保持一致
    void changeAttr(const wchar_t* src_name, const wchar_t* dst_name, HANDLE h_src, HANDLE h_dst)
    {
    	//修改文件属性
    	DWORD attr = GetFileAttributes(src_name);
    	SetFileAttributes(dst_name, attr);
    
    	//修改访问、修改时间
    	FILETIME t_create, t_access, t_write;
    	SYSTEMTIME syst_create, syst_access, syst_write;
    	GetFileTime(h_src, &t_create, &t_access, &t_write);
    	SetFileTime(h_dst, &t_create, &t_access, &t_write);
    }
    
    
    
  • 相关阅读:
    随时查询
    插入图片后R文件变红,报错“Error::app:mergeDebugResources'. > Some file crunching failed, see logs for detail”
    android 布局
    用SVN导入android项目时候没有导入default.properties这文件的解决方法
    Button的点击事件可以在XML文件中设置
    设置全屏有两种方式
    android 中加入的音乐文件有的时候没有播放。
    android spinner 调用xml里的数据
    刚刚申请了个博客发发感想
    .Net下WinForm换肤控件整理(转)
  • 原文地址:https://www.cnblogs.com/harrypotterjackson/p/12113382.html
Copyright © 2011-2022 走看看