主頁 > 作業系統 > 用Linux系統呼叫和C語言庫函式 兩種方式實作檔案拷貝(linux程式的IO操作)

用Linux系統呼叫和C語言庫函式 兩種方式實作檔案拷貝(linux程式的IO操作)

2020-09-23 09:07:11 作業系統

本文的撰寫主要是在了解,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程式設計》

 

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/110674.html

標籤:Linux

上一篇:編譯部署MySQL

下一篇:Linux Web服務器集群搭建

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • CA和證書

    1、在 CentOS7 中使用 gpg 創建 RSA 非對稱密鑰對 gpg --gen-key #Centos上生成公鑰/密鑰對(存放在家目錄.gnupg/) 2、將 CentOS7 匯出的公鑰,拷貝到 CentOS8 中,在 CentOS8 中使用 CentOS7 的公鑰加密一個檔案 gpg -a ......

    uj5u.com 2020-09-10 00:09:53 more
  • Kubernetes K8S之資源控制器Job和CronJob詳解

    Kubernetes的資源控制器Job和CronJob詳解與示例 ......

    uj5u.com 2020-09-10 00:10:45 more
  • VMware下安裝CentOS

    VMware下安裝CentOS 一、軟硬體準備 1 Centos鏡像準備 1.1 CentOS鏡像下載地址 下載地址 1.2 CentOS鏡像下載程序 點擊下載地址進入如下圖的網站,選擇需要下載的版本,這里選擇的是Centos8,點擊如圖所示。 決定選擇Centos8后,選擇想要的鏡像源進行下載,此 ......

    uj5u.com 2020-09-10 00:12:10 more
  • 如何使用Grep命令查找多個字串

    如何使用Grep 命令查找多個字串 大家好,我是良許! 今天向大家介紹一個非常有用的技巧,那就是使用 grep 命令查找多個字串。 簡單介紹一下,grep 命令可以理解為是一個功能強大的命令列工具,可以用它在一個或多個輸入檔案中搜索與正則運算式相匹配的文本,然后再將每個匹配的文本用標準輸出的格式 ......

    uj5u.com 2020-09-10 00:12:28 more
  • git配置http代理

    git配置http代理 經常遇到克隆 github 慢的問題,這里記錄一下幾種配置 git 代理的方法,解決 clone github 過慢。 目錄 git配置代理 git單獨配置github代理 git配置全域代理 配置終端環境變數 git配置代理 主要使用 git config 命令 git單獨 ......

    uj5u.com 2020-09-10 00:12:33 more
  • Linux npm install 裝包時提示Error EACCES permission denied解

    npm install 裝包時提示Error EACCES permission denied解決辦法 ......

    uj5u.com 2020-09-10 00:12:53 more
  • Centos 7下安裝nginx,使用yum install nginx,提示沒有可用的軟體包

    Centos 7下安裝nginx,使用yum install nginx,提示沒有可用的軟體包。 18 (flaskApi) [root@67 flaskDemo]# yum -y install nginx 19 已加載插件:fastestmirror, langpacks 20 Loading ......

    uj5u.com 2020-09-10 00:13:13 more
  • Linux查看服務器暴力破解ssh IP

    在公網的服務器上經常遇到別人爆破你服務器的22埠,用來挖礦或者干其他嘿嘿嘿的事情~ 這種情況下正確的做法是: 修改默認ssh的22埠 使用設定密鑰登錄或者白名單ip登錄 建議服務器密碼為復雜密碼 創建普通用戶登錄服務器(root權限過大) 建立堡壘機,實作統一管理服務器 統計爆破IP [root ......

    uj5u.com 2020-09-10 00:13:17 more
  • CentOS 7系統常見快捷鍵操作方式

    Linux系統中一些常見的快捷方式,可有效提高操作效率,在某些時刻也能避免操作失誤帶來的問題。 ......

    uj5u.com 2020-09-10 00:13:31 more
  • CentOS 7作業系統目錄結構介紹

    作業系統存在著大量的資料檔案資訊,相應檔案資訊會存在于系統相應目錄中,為了更好的管理資料資訊,會將系統進行一些目錄規劃,不同目錄存放不同的資源。 ......

    uj5u.com 2020-09-10 00:13:35 more
最新发布
  • vim的常用命令

    Vim的6種基本模式 1. 普通模式在普通模式中,用的編輯器命令,比如移動游標,洗掉文本等等。這也是Vim啟動后的默認模式。這正好和許多新用戶期待的操作方式相反(大多數編輯器默認模式為插入模式)。 2. 插入模式在這個模式中,大多數按鍵都會向文本緩沖中插入文本。大多數新用戶希望文本編輯器編輯程序中一 ......

    uj5u.com 2023-04-20 08:43:21 more
  • vim的常用命令

    Vim的6種基本模式 1. 普通模式在普通模式中,用的編輯器命令,比如移動游標,洗掉文本等等。這也是Vim啟動后的默認模式。這正好和許多新用戶期待的操作方式相反(大多數編輯器默認模式為插入模式)。 2. 插入模式在這個模式中,大多數按鍵都會向文本緩沖中插入文本。大多數新用戶希望文本編輯器編輯程序中一 ......

    uj5u.com 2023-04-20 08:42:36 more
  • docker學習

    ###Docker概述 真實專案部署環境可能非常復雜,傳統發布專案一個只需要一個jar包,運行環境需要單獨部署。而通過Docker可將jar包和相關環境(如jdk,redis,Hadoop...)等打包到docker鏡像里,將鏡像發布到Docker倉庫,部署時下載發布的鏡像,直接運行發布的鏡像即可。 ......

    uj5u.com 2023-04-19 09:26:53 more
  • 設定Windows主機的瀏覽器為wls2的默認瀏覽器

    這里以Chrome為例。 1. 準備作業 wsl是可以使用Windows主機上安裝的exe程式,出于安全考慮,默認情況下改功能是無法使用。要使用的話,終端需要以管理員權限啟動。 我這里以Windows Terminal為例,介紹如何默認使用管理員權限打開終端,具體操作如下圖所示: 2. 操作 wsl ......

    uj5u.com 2023-04-19 09:25:49 more
  • docker學習

    ###Docker概述 真實專案部署環境可能非常復雜,傳統發布專案一個只需要一個jar包,運行環境需要單獨部署。而通過Docker可將jar包和相關環境(如jdk,redis,Hadoop...)等打包到docker鏡像里,將鏡像發布到Docker倉庫,部署時下載發布的鏡像,直接運行發布的鏡像即可。 ......

    uj5u.com 2023-04-19 09:19:04 more
  • Linux學習筆記

    IP地址和主機名 IP地址 ifconfig可以用來查詢本機的IP地址,如果不能使用,可以通過install net-tools安裝。 Centos系統下ens33表示主網卡;inet后表示IP地址;lo表示本地回環網卡; 127.0.0.1表示代指本機;0.0.0.0可以用于代指本機,同時在放行設 ......

    uj5u.com 2023-04-18 06:52:01 more
  • 解決linux系統的kdump服務無法啟動的問題

    問題:專案麒麟系統服務器的kdump服務無法啟動,沒有相關日志無法定位問題。 1、查看服務狀態是關閉的,重啟系統也無法啟動 systemctl status kdump 2、修改grub引數,修改“crashkernel”為“512M(有的機器數值太大太小都會導致報錯,建議從128M開始試,或者加個 ......

    uj5u.com 2023-04-12 09:59:50 more
  • 解決linux系統的kdump服務無法啟動的問題

    問題:專案麒麟系統服務器的kdump服務無法啟動,沒有相關日志無法定位問題。 1、查看服務狀態是關閉的,重啟系統也無法啟動 systemctl status kdump 2、修改grub引數,修改“crashkernel”為“512M(有的機器數值太大太小都會導致報錯,建議從128M開始試,或者加個 ......

    uj5u.com 2023-04-12 09:59:01 more
  • 你是不是暴露了?

    作者:袁首京 原創文章,轉載時請保留此宣告,并給出原文連接。 如果您是計算機相關從業人員,那么應該經歷不止一次網路安全專項檢查了,你肯定是收到過資訊系統技術檢測報告,要求你加強風險監測,確保你提供的系統服務堅實可靠了。 沒檢測到問題還好,檢測到問題的話,有些處理起來還是挺麻煩的,尤其是線上正在運行的 ......

    uj5u.com 2023-04-05 16:52:56 more
  • 細節拉滿,80 張圖帶你一步一步推演 slab 記憶體池的設計與實作

    1. 前文回顧 在之前的幾篇記憶體管理系列文章中,筆者帶大家從宏觀角度完整地梳理了一遍 Linux 記憶體分配的整個鏈路,本文的主題依然是記憶體分配,這一次我們會從微觀的角度來探秘一下 Linux 內核中用于零散小記憶體塊分配的記憶體池 —— slab 分配器。 在本小節中,筆者還是按照以往的風格先帶大家簡單 ......

    uj5u.com 2023-04-05 16:44:11 more