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
判断文件类型
-
int lstat(const char *pathname, struct stat *statbuf);
-
const char *pathname
需要判断的文件的路径 -
struct stat *statbuf
用于保存文件属性 -
return int
0成功,-1失败
-
-
判断
文件类型 说明 判断函数 例子 普通文件 一般意义上的文件 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()
判断时会出现将其判断为普通文件的情况。可能是由于他判断的是链接文件所指向的文件的类型。因此需要先判断是不是链接文件。
遍历文件目录
-
dir *opendir(const char *name);
-
const char *name
待打开的目录路径 -
return dir
返回目录数据结构
-
-
struct dirent *readdir(dir *dirp);
```
dir *dirp
待读取的目录-
return struct dirent*
返回顺序读取的该目录下的目录项注意,第一个目录项是
.
, 第二个目录项是..
复制文件
-
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
返回值是打开的文件描述符
-
-
int creat(const char *pathname, mode_t mode);
-
const char* pathname
待创建的文件名(可以包含路径) -
mode_t mode
创建属性 -
return int
待创建文件描述符
-
-
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
-
-
ssize_t write(int fd, const void *buf, size_t count);
-
int fd
要写入的文件的描述符 -
const void *buf
要写入的内容。 -
size_t count
要写入的内容大小 -
return ssize_t
成功返回实际写入的数目。失败返回-1
-
复制文件夹
复制文件夹就是创建同名文件夹
-
int mkdir(const char *pathname, mode_t mode);
-
const char* pathname
待创建的目录项的地址 -
mode_t mode
创建目录项的属性 -
return int
如果成功返回为0,否则返回-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
返回值是软链接指向路径的长度
-
-
int symlink(const char *target, const char *linkpath);
-
const char* target
待创建的软链接的路径 -
const char* linkpath
待创建的软链接所指向的路径 -
return int
成功返回0,否则返回-1
-
获取属性
-
int lstat(const char *pathname, struct stat *statbuf);
-
const char *pathname
需要提取属性的文件或者文件夹的路径 -
struct stat *statbuf
获取到的属性存储缓冲区 -
return int
成功返回0,否则-1
-
-
int chmod(const char *pathname, mode_t mode);
-
const char *pathname
待修改属性的文件的路径 -
mode_t mode
将待修改文件修改改为该属性 -
return int
若成功返回0,否则返回-1
-
-
int chown(const char *pathname, uid_t owner, gid_t group);
-
const char* pathname
待更改的目录路径 -
uid_t owner
如果是-1则不改变属性 -
gid_t group
如果是-1则不改变属性
-
-
int lutimes(const char *filename, const struct timeval tv[2]);
这个命令用于修改目标文件的
access_time
modify_time
。lutimes
可以修改软链接的属性。-
const char *filename
待修改的文件路径 -
const struct timeval tv[2]
tv[0]
是access_time
,tv[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; }