本文的编写主要是在了解,Linux系统调用和C语言库函数的基础上进行的编写代码。
这篇文章将讲解Linux以下的系统调用:open()、read()、write()、close()、lseek()。涉及到的c语言库函数:fopen()、fread()、fwrite()、fclose()、flseek()。
用Linux系统调用和C语言库函数 两种方式实现文件拷贝
采用Linux系统调用实现文件拷贝
1.open()
用open函数可以打开或创建一个文件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <fcntl.h> 4 int open(const char *pathname , int oflag,.../*, mode_t mode * / ) ; 5 6 // 返回:若成功为文件描述符,若出错为- 1
pathname 是要打开或创建的文件的名字。oflag参数可用来说明此函数的多个选择项。用下列一个或多个常数进行或运算构成oflag参数(这些常数定义在<fcntl.h>头文件中):
•O_RDONLY 只读打开。
•O_WRONLY 只写打开。
•O_RDWR 读、写打开。
在这三个常数中应当只指定一个。下列常数则是可选择的:
•O_APPEND 每次写时都加到文件的尾端。
•O_CREAT 若此文件不存在则创建它。使用此选择项时,需同时说明第三个参数mode,用其说明该新文件的存取许可权位。
•O_EXCL 如果同时指定了O_CREAT,而文件已经存在,则出错。这可测试一个文件是否存在,如果不存在则创建此文件成为一个原子操作。
•O_TRUNC 如果此文件存在,而且为只读或只写成功打开,则将其长度截短为0。
•O_NOCTTY 如果pathname指的是终端设备,则不将此设备分配作为此进程的控制终端。
•O_NONBLOCK 如果pathname指的是一个FIFO、一个块特殊文件或一个字符特殊文件,则此选择项为此文件的本次打开操作和后续的I/O操作设置非阻塞方式。
•O_SYNC 使每次write都等到物理I/O操作完成。
2.read()
用read函数从打开文件中读数据。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <unistd.h> ssize_t read(int filedes, void *buff, size_t nbytes) ; //返回:读到的字节数,若已到文件尾为0,若出错为-1
如read成功,则返回读到的字节数。如已到达文件的尾端,则返回0。
有多种情况可使实际读到的字节数少于要求读字节数:
• 读普通文件时,在读到要求字节数之前已到达了文件尾端。例如,若在到达文件尾端之前还有30个字节,而要求读100个字节,则read返回30,下一次再调用read时,它将返回0(文件尾端)。
• 当从终端设备读时,通常一次最多读一行(第11章将介绍如何改变这一点)。
• 当从网络读时,网络中的缓冲机构可能造成返回值小于所要求读的字节数。
• 某些面向记录的设备,例如磁带,一次最多返回一个记录。
读操作从文件的当前位移量处开始,在成功返回之前,该位移量增加实际读得的字节数。
3.write()
用write函数向打开文件写数据
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <unistd.h> 2 ssize_t write(int f i l e d e s, const void * buff, size_t nbytes) ; 3 4 //返回:若成功为已写的字节数,若出错为- 1
其返回值通常与参数nbytes的值不同,否则表示出错。write出错的一个常见原因是:磁盘已写满,或者超过了对一个给定进程的文件长度限制。
对于普通文件,写操作从文件的当前位移量处开始。如果在打开该文件时,指定了O_APPEND选择项,则在每次写操作之前,将文件位移量设置在文件的当前结尾处。在一次成功写之后,该文件位移量增加实际写的字节数。
4.close()
可用close函数关闭一个打开文件:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <unistd.h> 2 int close (int filedes); 3 4 //返回:若成功为0,若出错为-1
5.lseek()
可以调用lseek显式地定位一个打开文件。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <sys/types.h> 2 #include <unistd.h> 3 off_t lseek(int filedes, off_t offset, int whence) ; 4 5 //返回:若成功为新的文件位移,若出错为- 1
对参数offset的解释与参数whence的值有关。
•若whence是SEEK_SET,则将该文件的位移量设置为距文件开始处offset个字节。
•若whence是SEEK_CUR,则将该文件的位移量设置为其当前值加offset,offset可为正或负。
•若whence是SEEK_END,则将该文件的位移量设置为文件长度加offset,offset可为正或负。
经过上面的简单学习我们可以采用Linux系统调用实现文件拷贝
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<stdio.h> 2 #include<sys/types.h> 3 #include<sys/stat.h> 4 #include<fcntl.h> 5 #include<unistd.h> 6 #include<stdlib.h> 7 #include<errno.h> 8 #include <string.h> 9 10 #define BUFF_SIZE 8192 11 12 int main(int argc,char **argv){ 13 14 int from_fd;//源文件的文件描述符 15 int to_fd;//目标文件的文件描述符 16 off_t file_size=0; 17 char buffer[BUFF_SIZE]; 18 int nread; 19 20 //判断参数个数是否正确 21 if(argc != 3) 22 { 23 printf("Usage:%s fromfile tofile ",argv[0]); 24 exit(1); 25 } 26 27 //打开源文件 28 if((from_fd=open(argv[1],O_RDONLY))==-1) 29 { 30 printf("Open %s Erron ",argv[1]); 31 exit(1); 32 } 33 34 //创建目标文件 35 if((to_fd=open(argv[2],O_CREAT|O_WRONLY|O_TRUNC,S_IRUSR|S_IWUSR))==-1) 36 { 37 printf("Open %s Erron ",argv[2]); 38 exit(1); 39 } 40 41 //测得文件的大小 42 file_size=lseek(from_fd,0L,SEEK_END); 43 lseek(from_fd,0L,SEEK_SET); 44 printf("from file size is =%ld ",file_size); 45 46 //进行文件拷贝 47 while((nread=read(from_fd,buffer,BUFF_SIZE)) > 0) 48 { 49 if((write(to_fd,buffer,nread)) !=nread) //将buffer中的数据写到目的文件 50 printf("write error"); 51 bzero(buffer,BUFF_SIZE); 52 } 53 close(from_fd); 54 close(to_fd); 55 exit(0); 56 return 0; 57 } 58
结果可以用diff命令进行文件的比对
采用C语言库函数实现文件拷贝
1.fopen()
fopen库函数类似于底层的open系统调用。它主要用于文件和终端的输入输出。如果你需要对设备进行明确的控制,那最好使用底层系统调用,因为这可以避免用库函数带来的一些潜在问题,如输入/输出缓冲。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 FILE *fopen(const char *pathname, const char * type) ; 3 4 //返回:若成功则为文件指针,若出错则为 N U L L
(1)fopen打开路径名由pathname指示的一个文件。
(2)type参数指定对该I/O流的读、写方式,ANSIC规定type参数可以有15种不同的值
2.fread()、
fread库函数用于从一个文件流里读取数据。数据从文件流stream读到由ptr指向的数据缓冲区里。fread和 fwrite都是对数据记录进行操作,size参数指定每个数据记录的长度,计数器nitems给出要传输的记录个数。它的返回值是成功读到数据缓冲区里的记录个数(而不是字节数)。当到达文件尾时,它的返回值可能会小于nitems,甚至可以是零。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 size_t fread(void *ptr, size_t size, size_t nitems, FILE * stream) ;
3.fwrite()、
fwrite库函数与fread有相似的接口。它从指定的数据缓冲区里取出数据记录,并把它们写到输出流中。它的返回值是成功写入的记录个数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio .h> 2 size_ t fwrite(const void *ptr, size_ t size, size_t nitems, FILE * stream) ;
4.fclose()、
fclose库函数关闭指定的文件流stream,使所有尚未写出的数据都写出。因为stdio库会对数据进行缓冲,所以使用fclose是很重要的。如果程序需要确保数据已经全部写出,就应该调用fclose函数。虽然当程序正常结束时,会自动对所有还打开的文件流调用fclose函数,但这样做你就没有机会检查由fclose报告的错误了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 int fclose(FILE * stream) ;
5.flseek()、
fseek函数是与lseek系统调用对应的文件流函数。它在文件流里为下一次读写操作指定位置。
offset和whence参数的含义和取值与前面的lseek系统调用完全一样。 但lseek返回的是一个off_t数值,而fseek返回的是一一个整数: 0表示成功,-1表示失败并设置errno指出错误。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 int fseek(FILE *stream, long int offset, int whence) ;
经过上面的简单学习我们可以采用C语言库函数实现文件拷贝
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<stdio.h> 2 #include <string.h> 3 #include<stdlib.h> 4 5 6 #define BUFFER_SIZE 1024 7 8 int main(int argc,char **argv) 9 { 10 11 FILE *from_fd=NULL; 12 FILE *to_fd=NULL; 13 int file_size=0; 14 char buffer[BUFFER_SIZE]; 15 16 //判断参数个数是否正确 17 if(argc != 3) 18 { 19 printf("Usage:%s fromfile tofile ",argv[0]); 20 exit(1); 21 } 22 23 //打开源文件 24 if(from_fd=fopen(argv[1],"r")==NULL) 25 { 26 printf("Open %s Erron ",argv[1]); 27 exit(1); 28 } 29 30 //创建目标文件 31 if(to_fd=open(argv[2],"wb+")==NULL) 32 { 33 printf("Open %s Erron ",argv[2]); 34 exit(1); 35 } 36 37 //测得文件大小 38 fseek(from_fd,0,SEEK_END); 39 file_size=ftell(from_fd); 40 printf("the from file size is %d ",file_size); 41 fseek(from_fd,0,SEEK_SET); 42 43 //进行文件的拷贝 44 while(!feof(from_fd)) 45 { 46 fread(buffer,BUFFER_SIZE,1,from_fd); 47 if(BUFFER_SIZE>=file_size) 48 fwrite(buffer,file_size,1,to_fd); 49 else 50 { 51 fwrite(buffer,BUFFER_SIZE,1,to_fd); 52 file_size=file_size-BUFFER_SIZE; 53 } 54 bzero(buffer,BUFFER_SIZE); 55 } 56 fclose(from_fd); 57 fclose(to_fd); 58 exit(0); 59 return 0; 60 }
结果可以用diff命令进行文件的比对
值得注意的是fseek()不像lseek()会返回读写位置, 因此必须使用ftell()来取得目前读写的位置。
本文涉及到的关于函数的解释大部分来自《Linux程序设计》