用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程序設計》

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM