最近在看国嵌的嵌入式视频,必修实验中的一个作业引起了我的兴趣:用库函数实现文件的复制
所以这几天都在写程序,虽然遇到不少问题,不过也算是学习的一点经历,因此写博来总结一下。
先贴实现的思路跟程序
<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,但是由于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]这种形式引用任意一个元素即可。)
+ 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在每次拷贝都会减小)。
解决:替换变量名即可。