问题描述

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

c++实现文件复制并修改相应属性的更多相关文章

  1. 使用PowerShell来修改文件访问,创建,修改时间属性

    Function Set-FileTimeStamps { Param ( [Parameter(mandatory=$true)] [string[]]$path, [datetime]$date ...

  2. C#实现把指定文件夹下的所有文件复制到指定路径下以及修改指定文件的后缀名

    1.实现把指定文件夹下的所有文件复制到指定路径下 public static void copyFiles(string path) { DirectoryInfo dir = new Directo ...

  3. 使用DOM解析XML文件,、读取xml文件、保存xml、增加节点、修改节点属性、删除节点

    使用的xml文件 <?xml version="1.0" encoding="GB2312" ?> <PhoneInfo> <Br ...

  4. 关于Entity Framework采用DB First模式创建后的实体批量修改相关属性技巧

    Entity Framework采用DB First模式创建实体是比较容易与方便的,修改已创建的实体在个数不多的情况下也是没问题的,但如果已创建的实体比较多,比如10个实体以上,涉及修改的地方比较多的 ...

  5. ASP FSO操作文件(复制文件、重命名文件、删除文件、替换字符串)

    ASP FSO操作文件(复制文件.重命名文件.删除文件.替换字符串)FSO的意思是FileSystemObject,即文件系统对象.FSO对象模型包含在Scripting 类型库 (Scrrun.Dl ...

  6. servlet操作本地文件汇总: 判断文件是否存在;文件重命名;文件复制; 获取文件属性信息,转成Json对象; 获取指定类型的文件; 查找替换.txt中的文本

    package servlet; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; ...

  7. C# 选择文件、选择文件夹、打开文件(或者文件夹) 路径中获取文件全路径、目录、扩展名、文件名称 追加、拷贝、删除、移动文件、创建目录 修改文件名、文件夹名!!

    https://www.cnblogs.com/zhlziliaoku/p/5241097.html 1.选择文件用OpenDialog OpenFileDialog dialog = new Ope ...

  8. boost_1_34_1在c++builder6编译(把所有dll文件复制到windows系统目录,所以lib文件复制到bcb6\lib目录)

    boost_1_34_1.zip boost 正则表达式 bcb6编译boost_1_34 有个项目要对大量的文本信息进行分析,以前的方法是自己写函数然后进行分析.现在发现一个正则表达式的处理方法,其 ...

  9. day05 创建用户过程、文件夹,文件等权限修改等

    linux创建用户时修改到的文件 /etc/passwd //存放用户信息 /etc/shadow //存放用户密码信息 /etc/group //存放用户组信息 /etc/gshadow //存放组 ...

随机推荐

  1. JavaScript 错误异常

    JavaScript 错误异常 错误异常语句 try 语句测试代码块中的错误 catch 语句处理错误 throw 语句允许自定义错误 finally 语句在错误异常语句后,必须执行的代码块 try ...

  2. 微信小程序初体验遇到的坑

    今天,2017年1月9日凌晨,微信小程序如约上线.2007年1月9日,整整10年前的今天,苹果的iPhone手机正式问世! 经不起新技术的诱惑了,想试着开发一下看看.刚开始遇到很多坑,在这里记录一下, ...

  3. 通过GL函数处理图片以及其它相关功能

    我们知道在屏幕后处理里面通过 Graphics.Blit 函数可以通过材质处理屏幕图片, 当我们想要处理一般图片的时候, 直接调用GL函数就行了, 按照习惯自己封装一个 Blit 方法 : ) { i ...

  4. DRF的基本使用(一)

    本帖最后由 杰哥,我就服你 于 2018-12-20 13:22 编辑 Django rest framework(DRF) D:是一个用于构建Web API强大又灵活的框架,基于Django框架二次 ...

  5. HTML与CSS学习笔记(6)

    1.flex弹性盒模型 在父容器中添加display:flex;把父容器变为弹性盒模型 作用(写)在flex容器上 作用(写)在flex子项上 flex-direction order flex-wr ...

  6. LeetCode 674. Longest Continuous Increasing Subsequence最长连续递增序列 (C++/Java)

    题目: Given an unsorted array of integers, find the length of longest continuous increasing subsequenc ...

  7. JPA简介

    JPA的学习 JPA是Java Persistence API的简称,中文名Java持久层API.是Java EE5.0平台中Sun为了统一持久层ORM框架而制定的一套标准,注意是一套标准,而不是具体 ...

  8. CentOS7 通过 devstack 安装 OpenStack

    安装前的准备 修改源 (可跳过) 将下载源变更到国内可以时下载速度大大提升 打开下面的文件 vim /etc/yum.repos.d/CentOS-Base.repo 将原来的注释掉改成: [base ...

  9. python 机器学习基础教程——第一章,引言

    https://www.cnblogs.com/HolyShine/p/10819831.html # from sklearn.datasets import load_iris import nu ...

  10. Ubuntu安装有道词典

    dpkg -i youdao-dict.deb正常安装 会报一堆依赖关系的错误, 1.更新系统 #更新系统 apt-get update apt-get dist-upgrade 2.对每一个未安装的 ...