用Linux系统调用和C语言库函数 两种方式实现文件拷贝(linux程序的IO操作)

本文的编写主要是在了解,Linux系统调用和C语言库函数的基础上进行的编写代码。

这篇文章将讲解Linux以下的系统调用:open()、read()、write()、close()、lseek()。涉及到的c语言库函数:fopen()、fread()、fwrite()、fclose()、flseek()。

用Linux系统调用和C语言库函数 两种方式实现文件拷贝

采用Linux系统调用实现文件拷贝

  1.open()

用open函数可以打开或创建一个文件

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
open()

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函数从打开文件中读数据。

#include <unistd.h>
ssize_t read(int filedes, void *buff, size_t nbytes) ;

//返回:读到的字节数,若已到文件尾为0,若出错为-1
read()

如read成功,则返回读到的字节数。如已到达文件的尾端,则返回0。
有多种情况可使实际读到的字节数少于要求读字节数:
• 读普通文件时,在读到要求字节数之前已到达了文件尾端。例如,若在到达文件尾端之前还有30个字节,而要求读100个字节,则read返回30,下一次再调用read时,它将返回0(文件尾端)。
• 当从终端设备读时,通常一次最多读一行(第11章将介绍如何改变这一点)。
• 当从网络读时,网络中的缓冲机构可能造成返回值小于所要求读的字节数。
• 某些面向记录的设备,例如磁带,一次最多返回一个记录。
读操作从文件的当前位移量处开始,在成功返回之前,该位移量增加实际读得的字节数。

   3.write()

用write函数向打开文件写数据

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
write

其返回值通常与参数nbytes的值不同,否则表示出错。write出错的一个常见原因是:磁盘已写满,或者超过了对一个给定进程的文件长度限制。
对于普通文件,写操作从文件的当前位移量处开始。如果在打开该文件时,指定了O_APPEND选择项,则在每次写操作之前,将文件位移量设置在文件的当前结尾处。在一次成功写之后,该文件位移量增加实际写的字节数。

  4.close()

可用close函数关闭一个打开文件:

1 #include <unistd.h> 
2 int close (int filedes);
3 
4 //返回:若成功为0,若出错为-1
View Code

  5.lseek()

可以调用lseek显式地定位一个打开文件。

1 #include <sys/types.h> 
2 #include <unistd.h> 
3 off_t lseek(int filedes, off_t offset, int whence) ;
4 
5 //返回:若成功为新的文件位移,若出错为- 1
lseek

对参数offset的解释与参数whence的值有关。
•若whence是SEEK_SET,则将该文件的位移量设置为距文件开始处offset个字节。
•若whence是SEEK_CUR,则将该文件的位移量设置为其当前值加offset,offset可为正或负。
•若whence是SEEK_END,则将该文件的位移量设置为文件长度加offset,offset可为正或负。

经过上面的简单学习我们可以采用Linux系统调用实现文件拷贝

 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\n",argv[0]);
24         exit(1);
25     }
26     
27     //打开源文件
28     if((from_fd=open(argv[1],O_RDONLY))==-1)
29     {
30         printf("Open %s Erron\n",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\n",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\n",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     
copy_1

结果可以用diff命令进行文件的比对

 

 采用C语言库函数实现文件拷贝

  1.fopen()

fopen库函数类似于底层的open系统调用。它主要用于文件和终端的输入输出。如果你需要对设备进行明确的控制,那最好使用底层系统调用,因为这可以避免用库函数带来的一些潜在问题,如输入/输出缓冲。

1 #include <stdio.h> 
2 FILE *fopen(const char *pathname, const char * type) ;
3 
4 //返回:若成功则为文件指针,若出错则为 N U L L
fopen

(1)fopen打开路径名由pathname指示的一个文件。
(2)type参数指定对该I/O流的读、写方式,ANSIC规定type参数可以有15种不同的值

r 或 r b   为读而打开
w 或 w b   使文件成为0长,或为写而创建
a 或 a b  添加;为在文件尾写而打开,或为写而创建
r+  或 r+b 或 rb+  为读和写而打开
w+ 或 w+b 或 wb+  使文件为0长,或为读和写而打开
a+  或 a+b 或 ab+  为在文件尾读和写而打开或创建

  2.fread()、

fread库函数用于从一个文件流里读取数据。数据从文件流stream读到由ptr指向的数据缓冲区里。fread和 fwrite都是对数据记录进行操作,size参数指定每个数据记录的长度,计数器nitems给出要传输的记录个数。它的返回值是成功读到数据缓冲区里的记录个数(而不是字节数)。当到达文件尾时,它的返回值可能会小于nitems,甚至可以是零。

1 #include <stdio.h>
2 size_t fread(void *ptr, size_t size, size_t nitems, FILE * stream) ;
fread

  3.fwrite()、

fwrite库函数与fread有相似的接口。它从指定的数据缓冲区里取出数据记录,并把它们写到输出流中。它的返回值是成功写入的记录个数。

1 #include <stdio .h>
2 size_ t fwrite(const void *ptr, size_ t size, size_t nitems, FILE * stream) ;
write

  4.fclose()、

fclose库函数关闭指定的文件流stream,使所有尚未写出的数据都写出。因为stdio库会对数据进行缓冲,所以使用fclose是很重要的。如果程序需要确保数据已经全部写出,就应该调用fclose函数。虽然当程序正常结束时,会自动对所有还打开的文件流调用fclose函数,但这样做你就没有机会检查由fclose报告的错误了。

1 #include <stdio.h>
2 int fclose(FILE * stream) ;
fclose

  5.flseek()、

fseek函数是与lseek系统调用对应的文件流函数。它在文件流里为下一次读写操作指定位置。
offset和whence参数的含义和取值与前面的lseek系统调用完全一样。 但lseek返回的是一个off_t数值,而fseek返回的是一一个整数: 0表示成功,-1表示失败并设置errno指出错误。

1 #include <stdio.h>
2 int fseek(FILE *stream, long int offset, int whence) ;
flseek

经过上面的简单学习我们可以采用C语言库函数实现文件拷贝

 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\n",argv[0]);
20         exit(1);
21     }
22     
23     //打开源文件
24     if(from_fd=fopen(argv[1],"r")==NULL)
25     {
26         printf("Open %s Erron\n",argv[1]);
27         exit(1);
28     }
29     
30     //创建目标文件
31     if(to_fd=open(argv[2],"wb+")==NULL)
32     {
33         printf("Open %s Erron\n",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\n",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 }
file_copy

结果可以用diff命令进行文件的比对

 

 

值得注意的是fseek()不像lseek()会返回读写位置, 因此必须使用ftell()来取得目前读写的位置。

 

本文涉及到的关于函数的解释大部分来自《Linux程序设计》

 

 

posted @ 2020-04-08 22:09  图南本南  阅读(5895)  评论(0编辑  收藏  举报