1. 程式人生 > >【Linux】C語言實現資料夾拷貝

【Linux】C語言實現資料夾拷貝

在《【Linux】利用C語言檔案流複製單一檔案》(點選開啟連結)講述瞭如何用C語言拷貝檔案,但是這隻能拷貝單一檔案。如果你要用LinuxC拷貝整個資料夾,同樣要像《【Java】利用檔案輸入輸出流完成把一個資料夾內的所有檔案拷貝的另一的資料夾的操作》(點選開啟連結)一樣,先用《【Linux】遍歷某一目錄,判斷檔案與資料夾,main引數》(點選開啟連結)的方法遍歷整個檔案目錄,之後再一個一個實現檔案拷貝。具體程式碼如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<dirent.h>//輸出檔案資訊
#include<sys/stat.h>//判斷是否目錄
int is_dir(char* path){//判斷是否是目錄
	struct stat st;
	stat(path,&st);
	if(S_ISDIR(st.st_mode)){
		return 1;
	}
	else{
		return 0;
	}
}
/*字串處理函式*/
int endwith(char* s,char c){//用於判斷字串結尾是否為“/”
	if(s[strlen(s)-1]==c){
		return 1;
	}
	else{
		return 0;
	}
}
char* str_contact(char* str1,char* str2){//字串連線
	char* result;
	result=(char*)malloc(strlen(str1)+strlen(str2)+1);//str1的長度+str2的長度+\0;
	if(!result){//如果記憶體動態分配失敗
		printf("字串連線時,記憶體動態分配失敗\n");
		exit(1);
	}
	strcat(result,str1);
	strcat(result,str2);//字串拼接
	return result;
}
/*複製函式*/
void copy_file(char* source_path,char *destination_path){//複製檔案
	char buffer[1024];
	FILE *in,*out;//定義兩個檔案流,分別用於檔案的讀取和寫入int len;
	if((in=fopen(source_path,"r"))==NULL){//開啟原始檔的檔案流
		printf("原始檔開啟失敗!\n");
		exit(1);
	}
	if((out=fopen(destination_path,"w"))==NULL){//開啟目標檔案的檔案流
		printf("目標檔案建立失敗!\n");
		exit(1);
	}
	int len;//len為fread讀到的位元組長
	while((len=fread(buffer,1,1024,in))>0){//從原始檔中讀取資料並放到緩衝區中,第二個引數1也可以寫成sizeof(char)
		fwrite(buffer,1,len,out);//將緩衝區的資料寫到目標檔案中
	}
	fclose(out);
	fclose(in);
}
void copy_folder(char* source_path,char *destination_path){//複製資料夾
	if(!opendir(destination_path)){
		if (mkdir(destination_path,0777))//如果不存在就用mkdir函式來建立
		{
		    printf("建立資料夾失敗!");
		}
	}
	char *path;
	path=(char*)malloc(512);//相當於其它語言的String path="",純C環境下的字串必須自己管理大小,這裡為path直接申請512的位置的空間,用於目錄的拼接
	path=str_contact(path,source_path);//這三句,相當於path=source_path
	struct dirent* filename;
	DIR* dp=opendir(path);//用DIR指標指向這個資料夾
	while(filename=readdir(dp)){//遍歷DIR指標指向的資料夾,也就是檔案陣列。
		memset(path,0,sizeof(path));
		path=str_contact(path,source_path);
		//如果source_path,destination_path以路徑分隔符結尾,那麼source_path/,destination_path/直接作路徑即可 
		//否則要在source_path,destination_path後面補個路徑分隔符再加檔名,誰知道你傳遞過來的引數是f:/a還是f:/a/啊?
		char *file_source_path;
		file_source_path=(char*)malloc(512);
		if(!endwith(source_path,'/')){
			file_source_path=str_contact(file_source_path,source_path);
			file_source_path=str_contact(source_path,"/");
		}
		else{
			file_source_path=str_contact(file_source_path,source_path);
		}
		char *file_destination_path;
		file_destination_path=(char*)malloc(512);
		if(!endwith(destination_path,'/')){
			file_destination_path=str_contact(file_destination_path,destination_path);
			file_destination_path=str_contact(destination_path,"/");
		}
		else{
			file_destination_path=str_contact(file_destination_path,destination_path);
		}
		//取檔名與當前資料夾拼接成一個完整的路徑
		file_source_path=str_contact(file_source_path,filename->d_name);
		file_destination_path=str_contact(file_destination_path,filename->d_name);
		if(is_dir(file_source_path)){//如果是目錄
			if(!endwith(file_source_path,'.')){//同時並不以.結尾,因為Linux在所有資料夾都有一個.資料夾用於連線上一級目錄,必須剔除,否則進行遞迴的話,後果無法相像
				copy_folder(file_source_path,file_destination_path);//進行遞迴呼叫,相當於進入這個資料夾進行復制~
			}		
		}
		else{
			copy_file(file_source_path,file_destination_path);//否則按照單一檔案的複製方法進行復制。
			printf("複製%s到%s成功!\n",file_source_path,file_destination_path);
		}
	}	
}
/*主函式*/
int main(int argc,char *argv[]){
	if(argv[1]==NULL||argv[1]==NULL){
		printf("請輸入兩個資料夾路徑,第一個為源,第二個為目的!\n");
		exit(1);
	}
	char* source_path=argv[1];//取使用者輸入的第一個引數
	char* destination_path=argv[2];//取使用者輸入的第二個引數
	DIR* source=opendir(source_path);
	DIR* destination=opendir(destination_path);
	if(!source||!destination){
		printf("你輸入的一個引數或者第二個引數不是資料夾!\n");
	}
	copy_folder(source_path,destination_path);//進行資料夾的拷貝
	return 0;
}

執行結果如下,設定一個有資料夾,有檔案的資料夾A,計劃將裡面的內容,拷貝到原本空空是也的資料夾B中。


上述程式中,難點有以下幾個:

1、由於此程式涉及多次檔案路徑的拼接,因此最大的難題就是C語言中對於字串的處理。眾所周知,C語言中是沒有字串的概念,僅有字元陣列,與指向這個字元陣列的首位置的指標的概念。用char *path;配合path=(char*)malloc(512);相當於搞出一個,指向長達512的空字元陣列的指標,此時path你可以理解為字串。這樣使用的strcat函式,不停地給path進行拼接,就不會出現段錯誤的記憶體溢位錯誤。

接下去用了大量的程式碼用於源與目標檔案路徑與檔名的拼接,其實完成的功能很簡單,如果是其它語言,可能string file_source_path;string filename->d_name;之後就一句file_source_path+filename->d_name;

反正大家明白什麼回事就行了,不必細究,我也不過是把《【Linux】純C環境下字串的處理》(點選開啟連結)中方法搞過來。

2、還有一個問題,就是複製的時候,要注意排除當前資料夾中.與..這兩個連結到上級目錄與根目錄的資料夾。在Linux似乎任何一個資料夾都有這個東東,一開始我寫程式的時候,根本不知道,搞了一個複雜度超高還根本停不下來的遞迴迭代,這也是Linux與Windows不一樣的地方。

3、其它地方也就沒什麼了,都是一些之前檔案寫過的東西。