專案要求
1.基本
用執行緒池實作一個大檔案夾的拷貝,大檔案夾嵌套很多小檔案;實作復制到指定檔案夾的全部檔案夾,
2.擴充功能
顯示進度條;拷貝耗時統計;類似linux的tree,不能直接用system()與exec()等函式;根據檔案型別拷貝;搜索檔案;洗掉檔案等,(暫時加了這么一些功能)
8月17日補:移動、復制到的目錄已包含該檔案則選擇覆寫或者加命名,
實作思路
先完成基本,逐步完成擴展再優化重構代碼,
實作程序
基本功能
基于linux,通過執行緒池實作的,核心就是執行緒池的三大基本功能--執行緒例程、添加執行緒、銷毀執行緒池,由這三個為基礎,對專案進行展開,基本功能,即通過遞回讀取目錄,通過strucr dirent *p這個結構體來實作判斷檔案型別,如果是普通檔案,直接在新目錄用檔案IO的讀寫實作拷貝功能(包括標準IO、系統IO,還有共享記憶體也可以實作),拷貝那里用“添加執行緒”,保證可以多執行緒實作拷貝;如果是目錄檔案,就先創建檔案夾--mkdir(),再sprintf拼接字串以及函式的遞回實作子級目錄的拷貝,
->拷貝代碼
void *myregcp(void *myarg) { struct copypath *mypath=(struct copypath *)myarg; //系統IO的復制 int fd1,fd2; fd1=open(mypath->oldpath,O_RDONLY); fd2=open(mypath->newpath,O_CREAT|O_TRUNC|O_RDWR,0777); if(fd1==-1) { perror("打開1失敗\n"); return NULL; } if(fd2==-1) { perror("打開2失敗\n"); return NULL; } char buf[SIZE]; int nread,nwrite; while(1) { bzero(buf,SIZE); nread=read(fd1,buf,SIZE); if(nread==0) break; cs=cs+nread; write(fd2,buf,nread); } close(fd1); close(fd2); return NULL; }
->遞回讀取全部目錄
int myreaddir(struct copypath *pp,struct threadpool *pool) { DIR *dirp=opendir(pp->oldpath); if(dirp==NULL) { perror("失敗:\n"); return -1; } struct dirent *p; while((p=readdir(dirp))!=NULL) { if(p->d_type==DT_REG) //普通檔案 { struct copypath *mypath=malloc(sizeof(struct copypath)); sprintf(mypath->oldpath,"%s/%s",pp->oldpath,p->d_name); sprintf(mypath->newpath,"%s/%s",pp->newpath,p->d_name); add_task(myregcp,mypath,pool); //實作 } if(p->d_type==DT_DIR) //檔案夾 { if(strcmp(p->d_name,".")!=0 && strcmp(p->d_name,"..")!=0) { struct copypath *mydirpath=malloc(sizeof(struct copypath)); sprintf(mydirpath->oldpath,"%s/%s",pp->oldpath,p->d_name); sprintf(mydirpath->newpath,"%s/%s",pp->newpath,p->d_name); mkdir(mydirpath->newpath,0777); myreaddir(mydirpath,pool); } } } closedir(dirp); return 1; }
這樣就大概完成基本功能,用多執行緒實作大檔案夾的拷貝,
擴充功能
->進度條
定義兩個全域變數,一個用于計算總位元組數,另一個計算每次復制的位元組數,再用一個顯示函式實作進度條的顯示,這里要注意緩沖區的問題,所以我用了"fflush(NULL)"這個函式,讓它每列印一個'|'的時候,就重繪一次緩沖區,計算總的位元組數直接遞回全部目錄,用"struct stat info"這個結構體里面的"info.st_size"累加,即可得到總的位元組數;拷貝功能函式里面有個"cs"變數,就是存放拷貝位元組數,顯示進度條就用簡單的判斷,加相除實作,因為是顯示20個|,所以我乘20,
num=(float)cs; //正在復制位元組數 k=(num/s)*20;
->耗時
有三種思路,用clock()、time()、sleep(1)等都可以實作計時,直接在拷貝前和拷貝后加賦值,然后相減,即可,起初自己是用clock()這個函式,但是每次都是三秒,,,然后轉到sleep(),讓它自己while()累加實作,
->代碼實作樹
還是遞回的思想,遞回如果是普通檔案就列印,是目錄檔案夾就字串拼接再遞回列印子檔案夾下的子檔案,
void dirtree(char dirpath[],int level) { int i; char *dirname=NULL; int dirlen; DIR *dp=opendir(dirpath); if(dp==NULL) { perror("失敗:\n"); return; } struct dirent *ep; while((ep=readdir(dp))!=NULL) { if(strncmp(ep->d_name, ".", 1) == 0) continue; for(i=0;i<level;i++) { printf("|"); printf(" "); } printf("|--- "); printf("\033[1;32;40m %s \033[0m\n",ep->d_name); if(ep->d_type==DT_DIR) { //當前目錄長度 dirlen=strlen(dirpath)+1; //備份 dirname=(char *)malloc(dirlen); memset(dirname,0,dirlen); memcpy(dirname,dirpath,dirlen); strcat(dirpath,"/"); strcat(dirpath,ep->d_name); dirtree(dirpath,level+1); //遞回實作樹效果 //恢復之前的目錄名 memcpy(dirpath,dirname,dirlen); free(dirname); dirname = NULL; } } closedir(dp); }
->按型別拷貝檔案
也比較簡單,另建一個遞回讀目錄的函式,在普通檔案加個if條件判斷就可以,
if(strstr(p->d_name,ftype)!=NULL)
->搜索檔案
類似于win的檔案檢索功能,在輸入那個檔案下面實作相似檔案名的檢索,并列印相關路徑,也是遞回讀取目錄,核心的改變代碼也就一句,用"strstr()"尋找相應的子字串,列印,
if(strstr(p->d_name,filename)!=NULL)
->洗掉檔案夾
洗掉稍微要注意一下如果檔案夾下面還有其他檔案,就不能直接用"rmdir"洗掉檔案夾,先用remove()洗掉檔案,之后再洗掉子檔案夾,也是用遞回思想實作的,
實作效果
現在還傳不了,后期再傳吧,
全部代碼
#include "myhead.h" #define SIZE 1024*1024 long int s=0; //總位元組數 long int cs=0; //計算每次復制的位元組數 int tm=0; //計時--以秒為單位 int flag; //結束標志位 /***************************相關結構體的定義******************************/ struct copypath { char oldpath[256]; char newpath[256]; char target[50]; }; //創建任務鏈表結構體 struct tasklist { void *(*taskp)(void *); void *taskarg; struct tasklist *next; }; //創建任務鏈表表頭 struct tasklist *myhead; //初始化任務鏈表 struct tasklist* task_init() { struct tasklist *mytask=malloc(sizeof(struct tasklist)); mytask->taskp=NULL; mytask->taskarg=NULL; mytask->next=NULL; return mytask; } //創建執行緒池 struct threadpool { int threadnum;//統計當前執行緒數量 pthread_t *threadid;//存放當前執行緒的ID號 struct tasklist *taskhead;//保存任務鏈表的頭結點 pthread_mutex_t threadmutex;//互斥鎖 int tasknum;//統計任務鏈表中的數量 pthread_cond_t threadcond;//條件變數 bool threadflag;//用于判斷執行緒池是否開啟 }; //執行緒的多任務函式 void *routine(void *arg) { struct threadpool *pool=(struct threadpool *)arg; //負責從任務鏈表的頭結點的下一個位置取出任務然后處理 struct tasklist *p; while(1) { //上鎖 pthread_mutex_lock(&(pool->threadmutex)); //判斷數量是否為0 while(pool->threadflag==true && pool->tasknum==0) { //printf("%ld執行緒阻塞--wait\n",pthread_self()); pthread_cond_wait(&(pool->threadcond),&(pool->threadmutex)); } if(pool->threadflag==false&&pool->tasknum==0) { pthread_mutex_unlock(&(pool->threadmutex)); pthread_exit(NULL); } //取出節點處理 p=pool->taskhead->next; pool->taskhead->next=p->next; p->next=NULL; //更新任務數量 pool->tasknum--; //printf("%ld執行緒正在執行任務\n",pthread_self()); //解鎖 pthread_mutex_unlock(&(pool->threadmutex)); (p->taskp)(p->taskarg); free(p); } } //初始化執行緒池結構體 struct threadpool *thread_init(int num) { struct threadpool *mythread=malloc(sizeof(struct threadpool));//申請堆空間 mythread->threadnum=num; mythread->threadid=malloc(num*sizeof(pthread_t)); //初始化鏈表的表頭 mythread->taskhead=myhead; //鎖初始化 pthread_mutex_init(&(mythread->threadmutex),NULL); //條件變數初始化 pthread_cond_init(&(mythread->threadcond),NULL); mythread->tasknum=0; mythread->threadflag=true; for(int i=0;i<num;i++) pthread_create(&(mythread->threadid[i]),NULL,routine,mythread); return mythread; } //添加任務函式 int add_task(void *(*p)(void *),void *newarg,struct threadpool *pool) { //找到尾部 struct tasklist *q=pool->taskhead; while(q->next!=NULL) q=q->next; //準備新結點 struct tasklist *newnode=malloc(sizeof(struct tasklist)); newnode->taskp=p; newnode->taskarg=newarg; newnode->next=NULL; //上鎖 pthread_mutex_lock(&(pool->threadmutex)); //尾插 q->next=newnode; //更新任務數量 pool->tasknum++; pthread_mutex_unlock(&(pool->threadmutex)); //喚醒條件 pthread_cond_signal(&(pool->threadcond)); return 0; } /***************************************************************************/ // 功能函式 /***************************************************************************/ //主目錄名 void *mycopyname(void *myarg) { struct copypath *mypath=(struct copypath *)myarg; char fpath[15]; //求出主目錄的名字 char temp[100]; strcpy(temp,mypath->oldpath); char *p=strtok(temp,"/"); //獲取檔案名 while(p!=NULL) { bzero(fpath,15); strcpy(fpath,p); p=strtok(NULL,"/"); } strcpy(((struct copypath *)myarg)->target,fpath); } //普通檔案復制 void *myregcp(void *myarg) { struct copypath *mypath=(struct copypath *)myarg; //系統IO的復制 int fd1,fd2; fd1=open(mypath->oldpath,O_RDONLY); fd2=open(mypath->newpath,O_CREAT|O_TRUNC|O_RDWR,0777); if(fd1==-1) { perror("打開1失敗\n"); return NULL; } if(fd2==-1) { perror("打開2失敗\n"); return NULL; } char buf[SIZE]; int nread,nwrite; while(1) { bzero(buf,SIZE); nread=read(fd1,buf,SIZE); //cs=cs+nread; if(nread==0) break; cs=cs+nread; write(fd2,buf,nread); } close(fd1); close(fd2); return NULL; } //遍歷讀目錄 int myreaddir(struct copypath *pp,struct threadpool *pool) { DIR *dirp=opendir(pp->oldpath); if(dirp==NULL) { perror("失敗:\n"); return -1; } struct dirent *p; while((p=readdir(dirp))!=NULL) { if(p->d_type==DT_REG) //普通檔案 { struct copypath *mypath=malloc(sizeof(struct copypath)); sprintf(mypath->oldpath,"%s/%s",pp->oldpath,p->d_name); sprintf(mypath->newpath,"%s/%s",pp->newpath,p->d_name); add_task(myregcp,mypath,pool); //實作 } if(p->d_type==DT_DIR) //檔案夾 { if(strcmp(p->d_name,".")!=0 && strcmp(p->d_name,"..")!=0) { struct copypath *mydirpath=malloc(sizeof(struct copypath)); sprintf(mydirpath->oldpath,"%s/%s",pp->oldpath,p->d_name); sprintf(mydirpath->newpath,"%s/%s",pp->newpath,p->d_name); mkdir(mydirpath->newpath,0777); myreaddir(mydirpath,pool); } } } closedir(dirp); return 1; } //遞回算位元組數 int size_sum(struct copypath *pp) { DIR *dirp=opendir(pp->oldpath); if(dirp==NULL) { perror("失敗:\n"); return -1; } struct dirent *p; struct stat info; while((p=readdir(dirp))!=NULL) { if(p->d_type==DT_REG) //普通檔案 { struct copypath *mypath=malloc(sizeof(struct copypath)); sprintf(mypath->oldpath,"%s/%s",pp->oldpath,p->d_name); //計算位元組數 stat(mypath->oldpath,&info); s=s+info.st_size; } if(p->d_type==DT_DIR) //檔案夾 { if(strcmp(p->d_name,".")!=0 && strcmp(p->d_name,"..")!=0) { struct copypath *mydirpath=malloc(sizeof(struct copypath)); sprintf(mydirpath->oldpath,"%s/%s",pp->oldpath,p->d_name); size_sum(mydirpath); } } } closedir(dirp); return 1; } //實作進度條 void *pro_bar(void *myarg) { int i,j=0; float k,num; printf("進度:"); while(1) { j=k; num=(float)cs; //正在復制位元組數 k=(num/s)*20; // k=(int)k; if(k-j>=1) { for(i=0;i<k-j;i++) { printf("\033[1;31;42m | \033[0m"); } fflush(NULL); } usleep(10000); if(s==cs) { printf("\033[1;31;42m | \033[0m"); fflush(NULL); usleep(200); break; } } printf("\n任務完成\n"); return NULL; } //lseep計時 void *my_time(void *myarg) { while(1) { sleep(1); tm++; if(s==cs) { break; } } } //顯示選擇拷貝型別界面 int showdow() { int n; printf("**********************\n"); printf(" 請輸入你要拷貝的檔案 \n"); printf(" 1.復制全部檔案; \n"); printf(" 2.選擇型別復制; \n"); printf(" 3.原目錄檔案樹; \n"); printf(" 4.查找某個檔案; \n"); printf(" 5.洗掉某個檔案. \n"); printf("**********************\n"); scanf("%d",&n); return n; } //遞回讀取檔案型別的位元組數 int ftypesize_sum(struct copypath *pp,char *ftype) { DIR *dirp=opendir(pp->oldpath); if(dirp==NULL) { perror("失敗:\n"); return -1; } struct dirent *p; struct stat info; while((p=readdir(dirp))!=NULL) { if(p->d_type==DT_REG) //普通檔案 { if(strstr(p->d_name,ftype)!=NULL) //檔案型別 { struct copypath *mypath=malloc(sizeof(struct copypath)); sprintf(mypath->oldpath,"%s/%s",pp->oldpath,p->d_name); //計算位元組數 stat(mypath->oldpath,&info); s=s+info.st_size; } } if(p->d_type==DT_DIR) //檔案夾 { if(strcmp(p->d_name,".")!=0 && strcmp(p->d_name,"..")!=0) { struct copypath *mydirpath=malloc(sizeof(struct copypath)); sprintf(mydirpath->oldpath,"%s/%s",pp->oldpath,p->d_name); ftypesize_sum(mydirpath,ftype); } } } closedir(dirp); return 1; } //遞回讀取檔案型別的目錄 int myftyper(struct copypath *pp,struct threadpool *pool,char *ftype) { DIR *dirp=opendir(pp->oldpath); if(dirp==NULL) { perror("失敗:\n"); return -1; } struct dirent *p; while((p=readdir(dirp))!=NULL) { if(p->d_type==DT_REG) //普通檔案 { if(strstr(p->d_name,ftype)!=NULL) { struct copypath *mypath=malloc(sizeof(struct copypath)); sprintf(mypath->oldpath,"%s/%s",pp->oldpath,p->d_name); sprintf(mypath->newpath,"%s/%s",pp->newpath,p->d_name); add_task(myregcp,mypath,pool); //實作 } } if(p->d_type==DT_DIR) //檔案夾 { if(strcmp(p->d_name,".")!=0 && strcmp(p->d_name,"..")!=0) { struct copypath *mydirpath=malloc(sizeof(struct copypath)); sprintf(mydirpath->oldpath,"%s/%s",pp->oldpath,p->d_name); sprintf(mydirpath->newpath,"%s/%s",pp->newpath,p->d_name); mkdir(mydirpath->newpath,0777); myftyper(mydirpath,pool,ftype); } } } closedir(dirp); return 1; } //樹實作 void dirtree(char dirpath[],int level) { int i; char *dirname=NULL; int dirlen; DIR *dp=opendir(dirpath); if(dp==NULL) { perror("失敗:\n"); return; } struct dirent *ep; while((ep=readdir(dp))!=NULL) { if(strncmp(ep->d_name, ".", 1) == 0) continue; for(i=0;i<level;i++) { printf("|"); printf(" "); } printf("|--- "); printf("\033[1;32;40m %s \033[0m\n",ep->d_name); if(ep->d_type==DT_DIR) { //當前目錄長度 dirlen=strlen(dirpath)+1; //備份 dirname=(char *)malloc(dirlen); memset(dirname,0,dirlen); memcpy(dirname,dirpath,dirlen); strcat(dirpath,"/"); strcat(dirpath,ep->d_name); dirtree(dirpath,level+1); //遞回實作樹效果 //恢復之前的目錄名 memcpy(dirpath,dirname,dirlen); free(dirname); dirname = NULL; } } closedir(dp); } //遞回查找相似檔案-查找檔案 int sc_file(struct copypath *pp,char *filename) { DIR *dirp=opendir(pp->oldpath); if(dirp==NULL) { perror("失敗:\n"); return -1; } struct dirent *p; while((p=readdir(dirp))!=NULL) { if(p->d_type==DT_REG) //普通檔案 { if(strstr(p->d_name,filename)!=NULL) //檔案型別 { struct copypath *mypath=malloc(sizeof(struct copypath)); sprintf(mypath->oldpath,"%s/%s",pp->oldpath,p->d_name); printf("已找到檔案路徑:%s\n",mypath->oldpath); free(mypath); } } if(p->d_type==DT_DIR) //檔案夾 { if(strcmp(p->d_name,".")!=0 && strcmp(p->d_name,"..")!=0) { if(strstr(p->d_name,filename)!=NULL) //檔案型別 { struct copypath *mypath1=malloc(sizeof(struct copypath)); sprintf(mypath1->oldpath,"%s/%s",pp->oldpath,p->d_name); printf("已找到檔案路徑:%s\n",mypath1->oldpath); free(mypath1); } struct copypath *mydirpath=malloc(sizeof(struct copypath)); sprintf(mydirpath->oldpath,"%s/%s",pp->oldpath,p->d_name); sc_file(mydirpath,filename); } } } closedir(dirp); return 1; } //洗掉--檔案\檔案夾 int alldet(struct copypath *pp) { DIR *dirp=opendir(pp->oldpath); struct dirent *p; while((p=readdir(dirp))!=NULL) { if(p->d_type==DT_REG) //普通檔案 { struct copypath *mypath=malloc(sizeof(struct copypath)); sprintf(mypath->oldpath,"%s/%s",pp->oldpath,p->d_name); remove(mypath->oldpath); } if(p->d_type==DT_DIR) //檔案夾 { if(strcmp(p->d_name,".")!=0 && strcmp(p->d_name,"..")!=0) { struct copypath *mydirpath=malloc(sizeof(struct copypath)); sprintf(mydirpath->oldpath,"%s/%s",pp->oldpath,p->d_name); alldet(mydirpath); rmdir(mydirpath->oldpath); } } } closedir(dirp); rmdir((pp->oldpath)); return 1; } /***************************************************************************/ /***************************************************************************/ // 簡化主程式 /***************************************************************************/ int simcopy_all(struct threadpool *mypool) //簡化主程式--復制全部 { tm=s=cs=0; struct copypath paths; //新舊路徑結構體 printf("請輸入源檔案\n"); scanf("%s",paths.oldpath); printf("請輸入目標檔案\n"); scanf("%s",paths.newpath); mycopyname(&paths); sprintf(paths.newpath,"%s/%s",paths.newpath,paths.target); mkdir(paths.newpath,0777); size_sum(&paths); //計算位元組數 usleep(20); printf("總位元組數:%ld\n",s); add_task(my_time,NULL,mypool); add_task(pro_bar,NULL,mypool); myreaddir(&paths,mypool); } void simcopy(struct threadpool *mypool) //簡化主程式--按檔案型別復制 { tm=s=cs=0; char ftype[6]; //檔案型別變數 struct copypath paths; printf("請輸入源檔案\n"); scanf("%s",paths.oldpath); printf("請輸入目標檔案\n"); scanf("%s",paths.newpath); mycopyname(&paths); sprintf(paths.newpath,"%s/%s",paths.newpath,paths.target); mkdir(paths.newpath,0777); printf("請輸入要復制的檔案型別,如.txt等\n"); scanf("%s",ftype); ftypesize_sum(&paths,ftype); printf("總位元組數:%ld\n",s); usleep(20); add_task(my_time,NULL,mypool); add_task(pro_bar,NULL,mypool); myftyper(&paths,mypool,ftype); } void simtree() //簡化主程式--檔案樹 { char direntName[256]; struct copypath paths; printf("請輸入源檔案\n"); scanf("%s",paths.oldpath); memset(direntName, 0, sizeof(direntName)); strcat(direntName, paths.oldpath); printf("%s\n",paths.oldpath); dirtree(direntName, 0); } void myscfile() //簡化主程式--查找 { char fname[256]; struct copypath paths; printf("請輸入查找檔案夾名\n"); scanf("%s",paths.oldpath); printf("請輸入要查找的檔案名\n"); scanf("%s",fname); sc_file(&paths,fname); usleep(10); } void mydel() //簡化主程式--洗掉 { tm=s=cs=0; char delfile[100]; struct copypath paths; printf("請輸入要洗掉的檔案夾\n"); scanf("%s",paths.oldpath); size_sum(&paths); //計算位元組數 printf("size is: %ld\n",s); alldet(&paths); usleep(10); } void endshow() //簡化主程式--結束 { printf("請輸入 \n"); printf(" 1--繼續 \n"); printf(" 2--退出 \n"); scanf("%d",&flag); } /***************************************************************************/ //銷毀執行緒池 int pool_destroy(struct threadpool *pool) { int i; //改變標志位,讓執行緒退出死回圈 pool->threadflag=false; //喚醒所有執行緒 pthread_cond_broadcast(&(pool->threadcond)); //回收所有執行緒 for(i=0;i<pool->threadnum;i++) { pthread_join(pool->threadid[i],NULL); //printf("%ld執行緒已被回收\n",pool->threadid[i]); } return 0; } int main() { struct copypath paths; while(1) { flag=0; system("clear"); myhead=task_init(); //初始化任務鏈表的表頭 struct threadpool *mypool=thread_init(10); //創建并初始化執行緒池 int ret=showdow(); switch(ret) { case 1: //拷貝全部 simcopy_all(mypool); pool_destroy(mypool); //執行緒池的銷毀 printf("拷貝花費:%d seconds\n",tm); break; case 2: //拷貝部分 simcopy(mypool); pool_destroy(mypool); printf("拷貝花費:%d seconds\n",tm); break; case 3: //顯示檔案樹 simtree(); break; case 4: //查找某個檔案--顯示相似的檔案 myscfile(); break; case 5: //洗掉某個檔案夾 mydel(); printf("delete done\n"); break; default: break; } endshow(); if(flag==1) continue; else if(flag==2) break; } system("clear"); usleep(20); return 0; }
有什么建議,歡迎聯系,郵箱:[email protected]
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/3559.html
標籤:Linux
上一篇:Linux top詳解
