现在的位置: 首页 > 综合 > 正文

文件复制的C语言实现

2018年07月12日 ⁄ 综合 ⁄ 共 5848字 ⁄ 字号 评论关闭

最近在看国嵌的嵌入式视频,必修实验中的一个作业引起了我的兴趣:用库函数实现文件的复制

所以这几天都在写程序,虽然遇到不少问题,不过也算是学习的一点经历,因此写博来总结一下。


先贴实现的思路跟程序

<span style="font-size:18px;">#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

#define BUFFER_SIZE 1024

int main(int argc,char *argv[])
{
		//define variables
		FILE *sfile = NULL,*dfile = NULL;
		struct stat sfile_info,dfile_info;
		char *ptr = NULL;
		char *buffer = (char *)malloc(sizeof(char)*BUFFER_SIZE),*sfile_name = NULL;
		int data_read = 0,data_write = 0,file_len = 0,tmp_len = 0,path_len = 0,pos = 0;
		int i = 0;
		unsigned int file_size_byte = 0,file_size_kb = 0,file_size_mb =0,file_size_gb = 0;
		char order ; 

		//initializion and judgement
		bzero(buffer,sizeof(buffer));
		if(ptr != NULL && sfile != NULL && dfile != NULL && sfile_name !=NULL)
				exit(1);
		
		//the judgement of parameter number
		if(argc == 1 )
		{
				printf("\nHey,Hey,boy,you don't tell me the names of the source file and the target file\n ");
				exit(1);
		}
		if(argc == 2)
		{
				printf("\n..........any more? The target file name TAT\n");
				exit(1);
		}
		
		//if the source file existed ?
		if(access(argv[1],F_OK)==-1)
		{
				printf("\nAre you kidding me? The source file do not exist!\n");
				exit(1);

		}

		//a file or a a directory 
		stat(argv[1],&sfile_info);
		if(S_ISDIR(sfile_info.st_mode))
		{

				printf("\n.......The first path is pointing to adirectory,give me the path of the source file...\n");
				exit(1);
		}

		if(access(argv[1],R_OK)==-1)
		{
				printf("\nEh,the source file can not be read....\n");
				exit(1);
		}
		
		//deal with the target file
		while(dfile == NULL)
		{
				//the path is pointing to a directory,then make a copy into the directory
				if( !stat(argv[2],&dfile_info) && S_ISDIR(dfile_info.st_mode) )
				{
						//get the name of the source file and add it to the end of the path 
						if((argv[1][0]) == '/')//absolute path
						{
								path_len = strlen(argv[1]);
								sfile_name = malloc(sizeof(char)*path_len + 1);
								for(i = path_len;argv[1][i] != '/';i --);
								
								//get the positon of '/'
								pos =  i + 1;
								for(i = 0; i <= path_len - pos;i ++)
								{
										sfile_name[i] = argv[1][pos + i];
								}
								sfile_name[i] = '\0';

						}
						else//present path
								sfile_name = argv[1];

						strcat(argv[2],sfile_name);
					
				}
			
				//the path is pointing to the file,then have a operation for the file(the file is belong to the absolute path or prensentpath)
				else if(access(argv[2],F_OK)==-1)
				{
						dfile = fopen(argv[2],"w+");
				}
				else
				{
						printf("\nYou're just kidding me...the target file exist,well,how do you do with it ?\n\n[C]ancel copy\n[R]ename the target file\n[O]verwrite it\n\n");
						scanf("%c",&order);
						while(order != 'c' && order !='C' && order !='r' && order != 'R' && order != 'o' && order != 'O')
						{
								printf("\nChoose a correct operation ,friend.\n");
								printf("\nYou're just kidding me...the target file exist,well,how do you do with it ?\n\n[C]ancel copy\n[R]ename the target file\n[O]verwrite it\n\n");
								scanf("%c",&order);
						}
						switch (order)
						{
								case 'c':
								case 'C':exit(0);
								case 'r':
								case 'R':
										printf("\nThe new name?:");
							   			scanf("%s",argv[2]);
										break;
								case 'o':
								case 'O':
										if(access(argv[2],R_OK) == -1)
										{
												printf("\nThe target file is too hard to overwrite......Check the permisson of youself\n");
												exit(1);
										}
										else
										{
												printf("\nOverwritting.....\n");
												dfile = fopen(argv[2],"w+");
												break;
										}

						};
				}	
		}
		
		//open the source file
		if(( sfile = fopen(argv[1],"r" )) == NULL)
		{
				printf("\nI can't open the source file.....\n");
				exit(1);
		}

		//get the lenth of the data
		fseek(sfile,0,SEEK_END);
		file_len = ftell(sfile);
		fseek(sfile,0,SEEK_SET);
		tmp_len = file_len;

		//read the data from source file
		printf("\n\nCopying..........\n\n");
		while(!feof(sfile))
		{
				fread(buffer,sizeof(char),BUFFER_SIZE,sfile);
				if(ftell(dfile) != file_len)
				{
						if(BUFFER_SIZE >= tmp_len)
						{
								fwrite(buffer,sizeof(char),tmp_len,dfile);
								file_size_byte += tmp_len;
						}
						else
						{
								fwrite(buffer,sizeof(char),BUFFER_SIZE,dfile);
								tmp_len -= BUFFER_SIZE;

								//calculate the size of the data and print the info during moving
								file_size_kb ++;
								if(file_size_kb > 1023)
								{
										
										file_size_mb ++;
										if(file_size_mb % 10 == 0)
												printf("I have copied %d MB from %s to %s\n",file_size_mb,argv[1],argv[2]);
										file_size_kb = 0;
										if(file_size_mb > 1023)
										{
												file_size_gb ++;
												file_size_mb = 0;
												if(file_size_gb > 1023)
														printf("I'm too tired to move data TAT,because 1 TB data have been copied....\n");
										}
								}
						}

				}
				bzero(buffer,BUFFER_SIZE);
		}
		
		//finish and press the message
		if(file_size_gb == 0)
		{
			if(file_size_mb == 0)
			{
					if(file_size_kb == 0)
					{
							printf("Hahaha! I have finished the job! %4d Byte data have been copied from \"%s\" to \"%s\"\n",file_size_byte,argv[1],argv[2]);
					}
					else
					{

							printf("Hahaha! I have finished the job! %4.2f KB data have been copied from \"%s\" to \"%s\"\n",file_size_kb + file_size_byte*1.0/1024,argv[1],argv[2]);
					}

			}
			else
			{

							printf("Hahaha! I have finished the job! %4.2f MB data have been copied from \"%s\" to \"%s\"\n",file_size_mb + file_size_kb*1.0/1024,argv[1],argv[2]);
			}
		}
		else
		{
	
							printf("Hahaha! I have finished the job! %4.2f GB data have been copied from \"%s\" to \"%s\"\n",file_size_gb + file_size_mb*1.0/1024,argv[1],argv[2]);
		}
		
		free(buffer);
		buffer = NULL;
		fclose(dfile);
		fclose(sfile);
		exit(0);

}</span>
其中遇到的问题:

一、在判断目标文件是否存在时,利用access(argv[2],F_OK)==-1进行判断,结果无论目标文件存在与否都会认定目标文件存在(输入参数为目标文件所在的目录的绝对路径)。

原因:后来写了个测试程序,发现access这个函数对目录也适用,所以即使是输入参数是目标文件所在目录,所以程序认为目标文件存在(Linux:大哥,这个文件(目录)是真的存在啊!。。。。),所以怀疑Linux也把目录视为文件(在一切都是文件 中说到:“甚至目录也是文件”)。
解决:利用stat函数获取路径所指文件属性,加以判断即可。
——————————————————————————————————————————————————————————————————————————————

二、用while(!feof(sfile))判断源文件是否已经到达末尾时发现,拷贝好的文件末尾总是会多出一些乱码,而且乱码大小正好等于真正数据的大小,同时报错segfault.

原因:上网查找资料才明白,在读完最后一个字符后,fp->flag仍然没有被置为_IOEOF,因而feof()仍然没有探测到文件结尾。直到再次调用fgetc()执行读操作,feof()才能探测到文件结尾。所以程序多执行了一次fread
fwrite,但是由于fread读到的数据早已超过了源文件,所以写入的数据是乱码,同时触发segfault错误。
feof()和EOF的用法——
C中文件结尾的判断
解决:将fwrite函数提前,获取源文件长度,并比较目标文件与源文件长度,作为循环判断条件,每写完一段数据,两者长度比较。

——————————————————————————————————————————————————————————
三、当输入参数是绝对路径时,需要获取源文件名称并叠加到目标文件路径中,获取文件名时需要对argv这个指针数组进行操作,如何引用指针数组中某个指针指向的字符串中的字符?

解决:尝试用(*argv[1] + i)的方式获取字符,结果在gdb里调试返回的是数字,应该是对指针数组理解错误了,我以为*argv[1]既然是字符串首地址,那+1就是指向下一个字符,经过gdb测试发现,*argv[1]+1返回的数值是字符串第一个字符的ASCII码+1,而argv[1]+1返回的是字符串第二字符到末尾,随后写了个测试程序,可以通过*argv[1]
+ i 的形式获取传入参数字符串中的某个字符,不过这种表示方式从原理上是好理解的,后来我直接用argv[1][i]的形式获取,这个看着更加习惯(实际上这些都是属于指针数组的知识点,包括多维数组、字符串数组、二维数组,这些数组本质上都是指针数组的不同表现,所以以后看到指针数组,直接用arr[i][j]这种形式引用任意一个元素即可。)

——————————————————————————————————————————————————————————

四、前一天改动过变量,第二天在用fwrite写入数据时发现,目标文件末尾还是出现了一段乱码

原因:经过gdb调试发现,fwrite(buffer,sizeof(char),file_len,dfile)中的file_len写错,应该是tmp_len,因为前一天改动程序时,添加了tmp_len变量存储文件长度,忘了将fwrite中的参数改过来,所以在最后一次copy数据段的时候,会拷进file_len大小的数据,而是实际上应该是要拷进tmp_len大小的数据(tmp_len在每次拷贝都会减小)。
解决:替换变量名即可。

抱歉!评论已关闭.