欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

c++实现文件复制并修改相应属性

程序员文章站 2023-11-02 15:22:34
问题描述 完成一个目录复制命令mycp,包括目录下的文件和子目录, 运行结果如下: 思路 这道题目主要涉及文件读写操作和属性修改。需要支持文件夹复制、文件复制,在Linux下还要支持软链接的复制。 思路如下: 获取待复制目录的绝对路径 根据绝对路径进行dfs或者bfs搜索所有子目录项 判断子目录是属 ......

问题描述

完成一个目录复制命令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

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

findfirstfile

findnextfile

createfile

getfilesize

readfile

writefile

_wmkdir

getfileattributes

setfileattributes

getfiletime

filetimetosystemtime

setfiletime

源代码实现

linux

#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 search_dfs(char *src_path, char *dest_path);
//复制文件
void copy_file(const char *src_file, const char *dst_file);
//复制文件夹
void copy_dir(const char *src_dir, const char *dst_dir);
//复制软链接
void copy_sln(const char *src_file, const char *dst_file);
//修改文件属性与源文件保持一致
void changeattr(const char *src, const char *dst);
//修改路径
void change_path(char *src, char *cat)
{
    strcat(src, (char *)"/");
    strcat(src, cat);
}

int main(int argc, char const *argv[])
{
    if (argc < 3)
    {
        printf("no file or directory specified\n");
        exit(-1);
    }
    if (argc > 3)
    {
        printf("too many arguments\n");
        exit(-1);
    }

    char src[1024], dest[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, 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, getcwd(null, 0)); //获取目标文件夹绝对路径
            chdir(current_dir);
            chdir(dest);
            // printf("src: %s\n", src);
            // printf("dest: %s\n", dest);
            chdir(src);
            search_dfs(src, dest);
        }
        else
        {
            printf("error. no destination directory.\n");
            exit(-1);
        }
    }

    else //文件直接复制
    {
        char dest[1024];
        lstat(argv[2], &state_of_entry);
        if (s_isdir(state_of_entry.st_mode)) //目录
        {
            strcpy(dest, getcwd(null, 0)); //获取目标文件夹绝对路径
        }
        else
        {
            strcpy(dest, "./");
            strcat(dest, argv[2]);
        }
        copy_file(argv[1], argv[2]);
    }

    return 0;
}

void search_dfs(char *src_path, char *dest_path)
{
    // printf("searching directory:    %s\n", getcwd(null, 0));
    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)
    {
        lstat(entry->d_name, &state_of_entry);
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
            continue;
        // printf("entry->d_name: %s\n", entry->d_name);
        if (s_islnk(state_of_entry.st_mode)) //符号链接
        {
            char src_file[1024];
            char dest_file[1024];
            strcpy(src_file, src_path);
            change_path(src_file, entry->d_name);
            strcpy(dest_file, dest_path);
            change_path(dest_file, entry->d_name);
            // printf("src file: %s\n", src_file);
            // printf("dest file: %s\n", dest_file);
            copy_sln(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);
            change_path(src_file, entry->d_name);
            strcpy(dest_file, dest_path);
            change_path(dest_file, entry->d_name);
            // printf("src file: %s\n", src_file);
            // printf("dest file: %s\n", dest_file);
            copy_file(src_file, dest_file);
        }
        else if (s_isdir(state_of_entry.st_mode)) //目录
        {
            char src[1024];
            char dest[1024];
            strcpy(src, src_path);
            change_path(src, entry->d_name);
            strcpy(dest, dest_path);
            change_path(dest, entry->d_name);
            // printf("src dir: %s\n", src);
            // printf("dest dir: %s\n", dest);
            copy_dir(src, dest);
            search_dfs(src, dest);
        }
    }
}

void copy_file(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 copy_dir(const char *src_dir, const char *dst_dir)
{
    mkdir(dst_dir, 0777);
    changeattr(src_dir, dst_dir);
}

void copy_sln(const char *src_file, const char *dst_file)
{
    char buf[1024];
    memset(buf, 0, sizeof(buf));
    int len = 0;
    if ((len = readlink(src_file, buf, sizeof(buf))) > 0)
    {
        printf("%s\n", buf);
        if (symlink(buf, dst_file) == -1)
        {
            perror("symlink");
        }
    }
    changeattr(src_file, dst_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);

    //修改文件访问、修改时间
    struct timeval time_buf[2];
    time_buf[1].tv_sec = attr_of_src.st_mtime;
    time_buf[0].tv_sec = attr_of_src.st_atime;
    if(lutimes(dst, time_buf) == -1)
    {
        printf("%s\n", dst);
        perror("lutimes");
    }

    struct utimbuf tbuf;
    tbuf.actime = attr_of_src.st_atime;
    tbuf.modtime = attr_of_src.st_mtime;
    utime(dst, &tbuf);

    struct stat dst_attr_of_src;
    lstat(dst, &dst_attr_of_src);
    if (dst_attr_of_src.st_mtime != attr_of_src.st_mtime)
        printf("%s : %d\n", dst, attr_of_src.st_mtime);
}

windows

#define _crt_secure_no_warnings
#include <iostream>
#include <windows.h>
#include <cstring>
#define buf_size 1024
using namespace std;

//文件夹复制采用dfs或者bfs进行搜索是等价的
void search_dfs(wchar_t* src_path, wchar_t* dest_path);
//复制文件
void copy_file(const wchar_t* src_file, const wchar_t* dst_file);
//复制文件夹
void copy_dir(const wchar_t* src_dir, const wchar_t* dst_dir);
//修改文件属性与源文件保持一致
void change_attr(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_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]);
    wcout << l"src_path" << src_path << endl;
    wcout << l"dst_path" << dst_path << endl;
    //wcscpy(src_path, l"e:\\薪火培训\\*");
    //wcscpy(dst_path, l"e:\\2333");
    search_dfs(src_path, dst_path);
    delete[] src_path;
    delete[] dst_path;
    return 0;
}

//复制文件
void copy_file(const wchar_t* src_file, const wchar_t* dst_file)
{
    handle h_src = ::createfile(
            src_file, 
            generic_read | generic_write, 
            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!\n");
        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);

    change_attr(src_file, dst_file, h_src, h_dst);

    closehandle(h_src);
    closehandle(h_dst);
    delete[] buffer;
    return;
}

//复制文件夹
void copy_dir(const wchar_t* src_dirr, const wchar_t* dst_dir)
{
    wchar_t* src_dir = new wchar_t[buf_size];
    wcscpy(src_dir, src_dirr);
    src_dir[wcslen(src_dir) - 2] = '\0';
    _wmkdir(dst_dir);
    handle h_src = createfile(
        src_dir,
        generic_read | generic_write,
        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!\n");
        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!\n");
        closehandle(h_dst);
        exit(-1);
    }
    change_attr(src_dir, dst_dir, h_src, h_dst);
    closehandle(h_src);
    closehandle(h_dst);
    return;
}


void search_dfs(wchar_t* src_path, wchar_t* dst_path)
{
    handle h_find;
    win32_find_data find_data;
    large_integer size;
    h_find = findfirstfile(src_path, &find_data);
    if (h_find == invalid_handle_value)
    {
        cout << "fail to find first file" << endl;
        return;
    }
    copy_dir(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] = '\0';
            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);

            copy_dir(n_src_path, n_dst_path);
            search_dfs(n_src_path, n_dst_path);
        }
        else
        {
            //size.lowpart = find_data.nfilesizelow;
            //size.highpart = find_data.nfilesizehigh;
            //wcout << find_data.cfilename << "\t" << size.quadpart << " bytes" << endl;
            
            wchar_t n_src_path[buf_size];
            wcscpy(n_src_path, src_path);
            n_src_path[wcslen(n_src_path) - 1] = '\0';
            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);

            copy_file(n_src_path, n_dst_path);
        }

    } while (findnextfile(h_find, &find_data));
    return;
}

//修改文件属性与源文件保持一致
void change_attr(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);

    filetimetosystemtime(&t_create, &syst_create);
    //cout << syst_create.wday << endl;

    setfiletime(h_dst, &t_create, &t_access, &t_write);

    getfiletime(h_dst, &t_create, &t_access, &t_write);
    filetimetosystemtime(&t_create, &syst_create);
    //cout << syst_create.wday << endl;
}