Linux系統編程:簡單文件IO操作

来源:https://www.cnblogs.com/ghostwu/archive/2017/12/25/8108438.html
-Advertisement-
Play Games

使用Linux的文件API,經常看見一個東西,叫做文件描述符. 什麼是文件描述符? (1)文件描述符其實實質是一個數字,這個數字在一個進程中表示一個特定的含義,當我們open打開一個文件時,操作系統在記憶體中構建了一些數據結構來表示這個動態文件,然後返回給應用程式一個數字作為文件描述符,這個數字就和我 ...


使用Linux的文件API,經常看見一個東西,叫做文件描述符.

什麼是文件描述符?

(1)文件描述符其實實質是一個數字,這個數字在一個進程中表示一個特定的含義,當我們open打開一個文件時,操作系統在記憶體中構建了一些數據結構來表示這個動態文件,然後返回給應用程式一個數字作為文件描述符,這個數字就和我們記憶體中維護這個動態文件的這些數據結構掛鉤綁定上了,以後我們應用程式如果要操作這一個動態文件,只需要用這個文件描述符進行區分。

(2)文件描述符就是用來區分一個程式打開的多個文件的。

(3)文件描述符的作用域就是當前進程,出了當前進程這個文件描述符就沒有意義了

(4)文件描述符fd的合法範圍是0或者一個正數,不可能是一個負數

(5)open返回的fd必須記錄好,以後向這個文件的所有操作都要靠這個fd去對應這個文件,最後關閉文件時也需要fd去指定關閉這個文件。如果在我們關閉文件前fd丟了,那麼這個文件就沒法關閉了也沒法讀寫了

1)打開與讀取文件

 1 #include <stdio.h>
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <fcntl.h>
 5 #include <unistd.h>
 6 
 7 int main(int argc, char const *argv[]) {
 8 
 9     int fd = -1; //文件描述符
10 
11     //打開文件
12     fd = open( "ghostwu.txt", O_RDWR );
13 
14     if ( -1 == fd ) {
15         printf("文件打開失敗\n");
16     }else {
17         printf("文件打開成功,fd=%d\n", fd );
18     }
19 
20     //讀取文件
21     int count = 0;
22     char buf[20];
23     count = read( fd, buf, 50 );
24     if ( -1 == count ) {
25         printf("文件讀取失敗\n");
26     }else {
27         printf("文件讀取成功,實際讀取的位元組數目為:%d\n內容為%s\n", count, buf );
28     }
29 
30     //關閉文件
31     close( fd );
32 
33     return 0;
34 }

需要在當前目錄下存在ghostwu.txt這個文件,否則打開的時候失敗,這裡涉及2個api

int open(const char *pathname, int flags);

open非常簡單,第一個參數就是文件路徑, 第二個是文件模式,在man手冊中還提供了其他幾種方式。

ssize_t read(int fd, void *buf, size_t count);

第一個參數為文件描述符,就是open返回的那個值

第二個參數buf用來存儲從文件中讀取的內容

第三個參數,表示希望從文件中讀取的內容( 註:這個count數字可以隨便給,最終以返回的實際數目(read的返回值)為準

2)打開與寫入文件

 1 #include <stdio.h>
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <fcntl.h>
 5 #include <unistd.h>
 6 #include <string.h>
 7 
 8 int main(int argc, char const *argv[]) {
 9 
10     int fd = -1; //文件描述符
11 
12     //打開文件
13     fd = open( "ghostwu.txt", O_RDWR );
14 
15     if ( -1 == fd ) {
16         printf("文件打開失敗\n");
17     }else {
18         printf("文件打開成功,fd=%d\n", fd );
19     }
20 
21     //寫文件
22     char buf[] = "I love Linux, Linux is very very good!!!";
23     int count = 0;
24     count = write( fd, buf, strlen( buf ) );
25     if ( -1 == count ) {
26         printf("文件寫入失敗\n");
27     }else {
28         printf("文件寫入成功,實際寫入的位元組數目為:%d\n", count);
29     }
30 
31     //關閉文件
32     close( fd );
33 
34     return 0;
35 }

ssize_t write(int fd, const void *buf, size_t count);

第一個參數為文件描述符,就是open返回的那個值

第二個參數buf用來存儲寫入的內容

第三個參數,表示希望寫入的文件大小

3)open的一些flag參數

1,只讀與只寫許可權

 1 #include <stdio.h>
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <fcntl.h>
 5 #include <unistd.h>
 6 
 7 int main(int argc, char const *argv[]) {
 8 
 9     int fd = -1; //文件描述符
10 
11     //打開文件, O_RDONLY:只讀許可權,打開之後的文件只能讀取,不能寫入
12     //打開文件, O_WRONLY:只寫許可權,打開之後的文件只能寫入,不能讀取
13     // fd = open( "ghostwu.txt", O_RDONLY );
14     fd = open( "ghostwu.txt", O_WRONLY );
15 
16     if ( -1 == fd ) {
17         printf("文件打開失敗\n");
18     }else {
19         printf("文件打開成功,fd=%d\n", fd );
20     }
21 
22     //讀取文件
23     int count = 0;
24     char buf[41];
25     count = read( fd, buf, 38 );
26     if ( -1 == count ) {
27         printf("文件讀取失敗\n");
28     }else {
29         printf("文件讀取成功,實際讀取的位元組數目為:%d\n內容為%s\n", count, buf );
30     }
31 
32     //關閉文件
33     close( fd );
34 
35     return 0;
36 }

2,清空與追加

 1 #include <stdio.h>
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <fcntl.h>
 5 #include <unistd.h>
 6 #include <string.h>
 7 
 8 int main(int argc, char const *argv[]) {
 9 
10     int fd = -1; //文件描述符
11 
12     //打開文件
13     //在O_RDWR模式下,對於一個已經存在的文件,且有內容,那麼寫入文件會覆蓋對應大小的源文件內容【不是完全覆蓋】
14     // fd = open( "ghostwu.txt", O_RDWR );
15     //在具有寫入許可權的文件中,使用O_TRUNC 會先把原來的內容清除,再寫入新的內容
16     // fd = open( "ghostwu.txt", O_RDWR | O_TRUNC );
17     //在具有寫入許可權的文件中,使用O_APPEND 會把新內容追加到原來內容的後面
18     // fd = open( "ghostwu.txt", O_RDWR | O_APPEND );
19 
20     //在具有寫入許可權的文件中,使用O_APPEND和O_TRUNC O_TRUNC起作用,會把原來的內容清除,再寫入新的內容
21     fd = open( "ghostwu.txt", O_RDWR | O_APPEND | O_TRUNC );
22 
23     if ( -1 == fd ) {
24         printf("文件打開失敗\n");
25         return -1;
26     }else {
27         printf("文件打開成功,fd=%d\n", fd );
28     }
29 
30     //寫文件
31     char buf[] = "new content";
32     int count = 0;
33     count = write( fd, buf, strlen( buf ) );
34     if ( -1 == count ) {
35         printf("文件寫入失敗\n");
36         return -1;
37     }else {
38         printf("文件寫入成功,實際寫入的位元組數目為:%d\n", count);
39     }
40 
41     //關閉文件
42     close( fd );
43 
44     return 0;
45 }

3,文件存在已否,創建文件與設置許可權

 1 #include <stdio.h>
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <fcntl.h>
 5 #include <unistd.h>
 6 #include <errno.h>
 7 
 8 int main(int argc, char const *argv[]) {
 9 
10     int fd = -1;
11 
12     // fd = open( "ghostwu.txt", O_RDWR | O_CREAT | O_EXCL );
13 
14     /*
15         文件不存在:
16             創建這個文件 並打開成功
17         文件存在:
18             再次運行時(文件已經創建成功,存在了), 這時打開失敗
19     */
20     // fd = open( "ghostwu.txt", O_RDWR | O_CREAT );
21 
22     fd = open( "ghostwu.txt", O_RDWR | O_CREAT | O_EXCL, 666 );
23 
24     if( -1 == fd ) {
25         printf("文件打開失敗,錯誤號:%d\n", errno );
26         perror( "open" );
27         return -1;
28     }else {
29         printf("文件打開成功\n");
30     }
31 
32     close( fd );
33 
34     return 0;
35 }

上面用到了一個函數perror,errno和perror:

1)errno就是error number,意思就是錯誤號碼。linux系統中對各種常見錯誤做了個編號,當函數執行錯誤時,函數會返回一個特定的errno編號來告訴我們這個函數到底哪裡錯了

2)errno是由操作系統來維護的一個全局變數,操作系統內部函數都可以通過設置errno來告訴上層調用者究竟剛纔發生了一個什麼錯誤

3)errno本身實質是一個int類型的數字,每個數字編號對應一種錯誤。當我們只看errno時只能得到一個錯誤編號數字,並不知道具體錯在哪裡,所以:linux系統提供了一個函數perror(意思print error),perror函數內部會讀取errno並且將這個不好認的數字直接給轉成對應的錯誤信息字元串,然後列印出來

4,lseek用來移動文件內部指針

簡單應用:統計文件大小

 1 #include <stdio.h>
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <fcntl.h>
 5 #include <unistd.h>
 6 #include <errno.h>
 7 
 8 int main(int argc, char const *argv[]) {
 9 
10     if ( argc != 2 ) {
11         printf("usage:%s %s\n", argv[0], "filename");
12         return -1;
13     }
14 
15     int fd = -1;
16 
17     fd = open( argv[1], O_RDWR );
18 
19     if( -1 == fd ) {
20         printf("文件打開失敗,錯誤號:%d\n", errno );
21         perror( "open" );
22         return -1;
23     }else {
24         printf("文件打開成功\n");
25     }
26 
27     //把指針移動到文件末尾,就是文件的大小
28     int count = lseek( fd, 0, SEEK_END );
29 
30     printf("文件大小為%d\n", count);
31 
32     close( fd );
33     return 0;
34 }

------------------------------------------分割線------------------------------------------

一、同一個進程,多次打開同一個文件,然後讀出內容的結果是: 分別讀【我們使用open兩次打開同一個文件時,fd1和fd2所對應的文件指針是不同的2個獨立的指針】

 1 #include <stdio.h>
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <fcntl.h>
 5 #include <errno.h>
 6 #include <unistd.h>
 7 
 8 int main(int argc, char const *argv[]) {
 9 
10     int fd1 = -1;
11     int fd2 = -1;
12     char buf1[20] = {0};
13     char buf2[20] = {0};
14     int count1 = 0;
15     int count2 = 0;
16 
17     fd1 = open( "ghostwu.txt", O_RDWR );
18 
19     if ( -1 == fd1 ) {
20         printf("文件打開失敗\n");
21         perror( "open" );
22         return -1;
23     }else {
24         printf("文件打開成功,fd1=%d\n", fd1);
25     }
26 
27     count1 = read( fd1, buf1, 5 );
28     if ( -1 == count1 ) {
29         printf( "文件讀取失敗\n" );
30         perror( "read" );
31     }else {
32         printf( "文件讀取成功,讀取的內容是%s\n", buf1 );
33     }
34 
35     fd2 = open( "ghostwu.txt", O_RDWR );
36 
37     if ( -1 == fd1 ) {
38         printf("文件打開失敗\n");
39         perror( "open" );
40         return -1;
41     }else {
42         printf("文件打開成功,fd2=%d\n", fd1);
43     }
44 
45     count2 = read( fd2, buf2, 10 );
46     if ( -1 == count2 ) {
47         printf( "文件讀取失敗\n" );
48         perror( "read" );
49     }else {
50         printf( "文件讀取成功,讀取的內容是%s\n", buf2 );
51     }
52 
53     close( fd1 );
54     close( fd2 );
55 
56     return 0;
57 }

 二、同一個進程,多次打開同一個文件,然後寫入內容的結果是: 分別寫,當使用O_APPEND,就是接著寫

 1 #include <stdio.h>
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <fcntl.h>
 5 #include <errno.h>
 6 #include <unistd.h>
 7 #include <string.h>
 8 
 9 int main(int argc, char const *argv[]) {
10 
11     int fd1 = -1;
12     int fd2 = -1;
13     char buf1[] = "ghost";
14     char buf2[] = "wu";
15     int count1 = 0;
16     int count2 = 0;
17 
18     fd1 = open( "ghostwu.txt", O_RDWR );
19 
20     if ( -1 == fd1 ) {
21         printf("文件打開失敗\n");
22         perror( "open" );
23         return -1;
24     }else {
25         printf("文件打開成功,fd1=%d\n", fd1);
26     }
27 
28     count1 = write( fd1, buf1, strlen( buf1 ) );
29     if ( -1 == count1 ) {
30         printf( "文件寫入失敗\n" );
31         perror( "write" );
32     }else {
33         printf( "文件寫入成功,寫入的內容是%s\n", buf1 );
34     }
35 
36     fd2 = open( "ghostwu.txt", O_RDWR );
37 
38     if ( -1 == fd1 ) {
39         printf("文件打開失敗\n");
40         perror( "open" );
41         return -1;
42     }else {
43         printf("文件打開成功,fd2=%d\n", fd1);
44     }
45 
46     count2 = write( fd2, buf2, strlen( buf2 ) );
47     if ( -1 == count2 ) {
48         printf( "文件寫入失敗\n" );
49         perror( "write" );
50     }else {
51         printf( "文件寫入成功,寫入的內容是%s\n", buf2 );
52     }
53 
54     close( fd1 );
55     close( fd2 );
56 
57     return 0;
58 }
View Code

 

上面代碼保持不變,再寫入的時候加入flag標誌:

fd1 = open( "ghostwu.txt", O_RDWR | O_APPEND );

fd2 = open( "ghostwu.txt", O_RDWR | O_APPEND );

三、 dup後的fd和原來打開文件的fd指向的是同一個文件,同時對這個文件寫入時,是接著寫

 1 #include <stdio.h>
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <fcntl.h>
 5 #include <unistd.h>
 6 #include <string.h>
 7 
 8 int main(int argc, char const *argv[]) {
 9 
10     int fd1 = -1;
11     int fd2 = -1;
12 
13     fd1 = open( "ghostwu.txt", O_RDWR );
14 
15     if ( -1 == fd1 ) {
16         perror( "open" );
17         return -1;
18     }else {
19         printf("文件打開成功:fd=%d\n", fd1);
20     }
21 
22     //dup後的文件,同時write 是接著寫入
23     fd2 = dup( fd1 );
24     printf("文件dup成功:fd=%d\n", fd2);
25 
26     //分別向fd1和fd2指向的文件寫入
27 
28     char buf1[] = "ghost";
29     char buf2[] = "wu";
30 
31     int count1 = -1, count2 = -1;
32 
33     while ( 1 ) {
34         count1 = write( fd1, buf1, strlen( buf1 ) );
35         if ( -1 == count1 ) {
36             perror( "buf1->write" );
37             return -1;
38         }else {
39             printf("buf1->文件寫入成功\n");
40         }
41 
42         sleep( 1 );
43 
44         count2 = write( fd2, buf2, strlen( buf2 ) );
45         if ( -1 == count2 ) {
46             perror( "buf2->write" );
47             return -1;
48         }else {
49             printf("buf2->文件寫入成功\n");
50         }
51     }
52 
53     close( fd1 );
54     close( fd2 );
55     return 0;
56 }
View Code

 

在linux系統中,內核占用了0、1、2這三個fd,當我們運行一個程式得到一個進程時,內部就預設已經打開了3個文件,

對應的fd就是0、1、2。分別叫stdin、stdout、stderr。也就是標準輸入、標準輸出、標準錯誤。接下來,我們把標準輸出關閉,printf就不會輸出,如果用dup複製原來的fd,那麼新dup出來的fd就是1(對應標準輸出)

之後標準輸出的內容都會被寫入到原來fd對應的那個文件

 1 #include <stdio.h>
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <fcntl.h>
 5 #include <unistd.h>
 6 #include <string.h>
 7 
 8 int main(int argc, char const *argv[]) {
 9 
10     int fd = -1;
11 
12     fd = open( "ghostwu2.txt", O_RDWR );
13     if ( -1 == fd ) {
14         perror( "open" );
15         return -1;
16     }else {
17         printf( "文件打開成功fd=%d\n", fd );
18     }
19 
20     //fd=0 對應stdin  fd=1 對應 stdout  fd=2 對應stderror
21     close( 1 ); //關閉fd=1的標準輸出之後,printf輸出看不見
22 
23     int newFd = -1;
24 
25     newFd = dup( fd ); //newFd一定是1, 因為分配後的fd從最小的沒被占用的開始
26     char buf[3];
27     sprintf( buf, "%d", newFd ); //newFd轉字元串型
28     printf( "這是一段輸出,由於newFd和fd關聯到標準輸出(newFd=1),會被寫入到文件\n" );
29     write( fd, buf, 1 );
30 
31     return 0;
32 }
View Code

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • openSUSE 快速配置 面向使用Tumbleweed的新手,希望能讓你用 更少 的時間來配置openSUSE,拿出 更多 的時間去做該做的事! Leap看這裡 "Leap新手指南"
  • 訪問node.js官網(https://nodejs.org/en/download/),下載相應的版本。 下載完,點擊安裝 【預設目錄】 Node.js v8.9.3 to /usr/local/bin/node npm v5.5.1 to /usr/local/bin/npm 安裝完畢 node ...
  • 1、 再開機啟動的時候按鍵盤上的“e”鍵會出現如下界面。再次按”e“鍵進行選擇相應的內核。 2、 選擇相應的內核,再次按“e“。 3、經過第二步,這個畫面可以編輯,在信息的最後加“空格“,然後鍵入”single“(如圖):或者直接輸入數字的”1“並回車確定進入下一步。如下圖: 4、操作完第三步,會出 ...
  • 一、Apache簡介 Apache HTTP Server(簡稱Apache)是Apache軟體基金會的一個開放源代碼的網頁伺服器軟體,可以在大多數電腦操作系統中運行,由於其跨平臺和安全性(儘管不斷有新的漏洞被髮現,但由於其開放源代碼的特點,漏洞總能被很快修補。因此總合來說,其安全性還是相當高的。) ...
  • 轉:http://blog.csdn.net/lichengtongxiazai/article/details/38941913 此文章針對高通msm8953平臺,啟動過程中,bootloader(預設是bootable/bootloader/lk)會根據機器硬體信息選擇合適的devicetree ...
  • mv(選項)(參數) 剪切,或在同目錄下移動重命名1.如果目標文件是文件夾,則源文件直接移動到該文件夾內,名字還是源文件的名字。2.如果目標文件時文件,則源文件移動的同時也會更改名字3.如果源文件為多個,則目標必須是目錄,並且統一移動到目錄下 -b:當目標文件存在時,先進行備份在覆蓋 -f:當目標文 ...
  • 1.gzip壓縮工具 格式:gzip [-d#] filename -d:為解壓縮參數 #:#為數字,表示壓縮等級,1為最差,9為最好,6為預設 壓縮後,原文件會刪除 加入-d參數為解壓縮 主意:gzip只能壓縮文件,不能壓縮目錄 2.bzip2壓縮工具 格式:bzip2 [-dz] filenam ...
  • CleanMyMac 3是Mac系統中一個功能強大的應用程式清理軟體,它能夠優化Mac系統,合理釋放系統資源的開銷。它有一系列的工具,包括系統清理,在Mac上刪除應用程式的卸載程式,不留下任何東西,碎紙機刪除文件沒有痕跡,一套優化調整方案等等。 聽起來不錯?那麼,它也很好看。 CleanMyMac關 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...