Linux C語言標準輸入輸出
標準 I/O 庫(stdio)及其標頭檔案 stdio.h 為底層 I/O 系統呼叫提供了一個通用的介面。這個庫現在已經成為 ANSI 標準 C 的一部分。標準 I/O 庫提供了許多複雜的函式用於格式化輸出和掃描輸入。在很多方面,你使用的標準 I/O 庫的方式和使用底層檔案描述符一樣,需要先開啟一個檔案以建立一個訪問路徑,這個操作的返回值將作為其他 I/O 庫函式的引數。在標準 I/O 庫中,與底層檔案描述符對應的是 流 (stream,需要注意的是這個流與 C++ 中的輸入輸出流不一樣),它被實現為指向結構 FILE 的指標( 檔案指標 )。
在啟動程式時,有 3 個 檔案流 是自動開啟的,它們是 stdin、stdout 和 stderr ,在 stdio.h 中定義,分別代表著 標準輸入、標準輸出和標準錯誤輸出 ,與底層檔案描述符 0、1、2 相對應。可用的檔案流數量與檔案描述符一樣,都是有限制的,實際的限制由標頭檔案 stdio.h 中定義的 FOPEN_MAX 來定義,它的值至少為 8,在 Linux 系統中,通常是 16。
一、fopen 函式
用於檔案和終端的輸入輸出,,類似於系統呼叫 open。呼叫 fopen 函式成功時,返回一個非空的 FILE * 指標,呼叫失敗時,返回 NULL。函式原型如下:
#include <stdio.h>
FILE *fopen(const char *filename,const char *mode);
關於 FILE 結構,有:
typedef struct _iobuf{
int cnt; // 剩餘字元數
char *ptr; // 下一個字元的位置
char *base; // 緩衝區的位置
int flag; // 檔案訪問模式
int fd; // 檔案描述符
}FILE;
fopen 開啟由 filename 引數指定的檔案,並把它與一個檔案流關聯起來。mode 引數指定檔案開啟的方式,可以取以下的一些值:
字串 | 說明 |
"r" 、"rb" | 以只讀方式開啟檔案 |
"w" 、"wb" | 以寫方式開啟檔案,並把檔案長度截短為零 |
"a" 、"ab" | 以寫方式開啟檔案,新內容追加在檔案尾 |
"r+" 、"rb+" 、"r+b" | 以更新方式開啟檔案(讀和寫) |
"w+" 、"wb+" 、"w+b" | 以更新方式開啟檔案,並把檔案長度截短為零 |
"a+" 、"ab+" 、"a+b" | 以更新方式開啟檔案,新內容追加在檔案尾 |
其中,字母 b 表示檔案是一個二進位制檔案而不是文字檔案。另外,需要注意的是,mode 引數是一個字串而不是一個字元,因此總是需要使用雙引號 " " 括起來。
二、fread 函式
fread 函式用於從一個檔案流裡讀取資料。呼叫 fread 成功時,返回成功讀到緩衝區裡的記錄個數(不是位元組數)。函式原型如下:
#include <stdio.h>
size_t fread(void *ptr,size_t size,size_t nitems,FILE *stream);
資料從檔案流 stream 讀到由 ptr 指向的資料緩衝區裡,size 引數指定每個資料記錄的長度,計數器 nitems 給出要傳輸的記錄個數。舉個例子:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int buffer[10];
int i;
int counter;
counter = fread(buffer,1,10,stdin);
printf("讀取到緩衝區的記錄個數為:%d\n",counter);
fwrite(buffer,sizeof(int),1,stdout);
exit(0);
}
這段程式碼從標準輸入讀取 10 個 字元到緩衝區中,然後呼叫 fwrite 函式將緩衝區中的前 4 個字元寫到標準輸出。
三、fwrite 函式
fwrite 函式從指定的資料緩衝區裡取出資料記錄,並把它們寫到輸出流中,返回成功寫入的記錄個數,其引數與 fread 函式的引數類似。
#include <stdio.h>
siize_t fwrite(const void *ptr,size_t size,size_t nitems,FILE *stream);
需要注意的是, 用 fwrite 寫的檔案在不同的計算機體系結構之間可能不具備可移植性 ,因此,不推薦把 fread 和 fwrite 用於結構化資料。演示程式碼如下:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
char buffer[] = "hello,my name is Linux公社www.linuxidc.com";
fwrite(buffer,1,sizeof(buffer),stdout);
exit(0);
}
測試下
[linuxidc@localhost linuxidc.com]$ ./linuxidc
輸出:
hello,my name is Linux公社www.linuxidc.com
這段程式碼將 buffer 中的內容寫入標準輸出。
四、fclose 函式
fclose 函式關閉指定的檔案流 stream,使所有尚未寫出的資料都寫出。因為 stdio 庫會對資料進行緩衝,所以使用 fclose 是很重要的。如果程式需要確保資料已全部寫出,則應該呼叫 fclose 函式。
#include <stdio.h>
int fclose(FILE *stream);
五、fflush 函式
fflush 函式的作用是把檔案流裡所有未寫出的資料立刻寫出。
#include <stdio.h>
int fflush(FILE *stream);
在 呼叫 fclose 函式時,隱含執行了一次 flush 操作,因此不需要再呼叫 fclose 之前呼叫 fflush。
六、fseek 函式
fseek 函式是與 lseek 系統呼叫對應的檔案流函式。它在檔案流裡為下一次讀寫操作指定位置。當呼叫 fseek 成功時,返回 0,呼叫失敗時 返回 -1 並設定 errno 指出錯誤。
#include <stdio.h>
int fseek(FILE *stream,long int offset,int whence);
七、fgetc、getc 和 getchar 函式
fgetc 函式從檔案流裡取出下一個位元組,並把它作為一個字元返回(返回的是該字元的 ASCII 碼)。當它到達檔案尾或出現錯誤時,返回 EOF(在 stdio.h 中有定義: #define EOF -1)。必須通過 ferror 或 feof 來區分這兩種情況。函式原型如下:
#include <stdio.h>
int fgetc(FILE *stream);// 注意,返回值是一個字元,但是這裡用 int 型別來存放字元而不是用 char 型別
int getc(FILE *stream);
int getchar();
getc 函式的作用和 fgetc 函式一樣,但是,getc 函式可以被實現為巨集,因此:
1)getc 函式的引數不應該是具有副作用(如影響變數)的表示式,因為它可能會被計算多次;
2)fgetc 一定是一個函式,因此它的地址可以作為一個引數傳遞給另一個函式;而 gets 函式則不能保證其地址一定能作為一個函式指標傳遞給另一個函式;
3)由於 getc 函式可以被實現為巨集,因此呼叫 getc 函式所用的時間可能會比 fgetc 要短。
getchar 函式的作用就相當於 getc(stdin),它直接從標準輸入裡讀取下一個字元。
注意:為什麼返回值是四個位元組的 int 型別?
這是為了考慮檔案結束標誌 EOF 的取值。EOF 取值是 -1,如果用 unsigned 型別的話,顯然取不到 -1;而如果用 char 型別的話,則 -1 對應的值為 0xff(C 語言中,數值以補碼形式儲存),但是 0xff 又可以是一個位元組的 ASCII 碼值(一些擴充套件字元的 ASCII > 127溢位時,可能會產生值為 0xff 的ASCII 碼),這樣用 EOF 顯然就不能判斷檔案是否結束了,因為會把 ASCII 碼值為 0xff 的位元組誤判為檔案結束符。如果將返回值用 int 型別來存放的話,那麼 EOF(也就是 -1)將會被儲存為 0xffffffff,這時在讀到 0xff,用 int 型別進行儲存的話,就是 0x000000ff,就不會和 EOF 相沖突了。
八、fputc、putc 和 putchar 函式
fputc 函式把一個字元寫到一個輸出檔案流中。它返回寫入的值,如果失敗,則返回 EOF。
#include <stdio.h>
int fputc(int c,FILE *stream);// 需要注意的是,這裡的 c 其實是字元,把字元當做 int 型別而不是 char 型別
int putc(int c,FILE *stream);
int putchar(int c);
putc 函式的作用與 fputc 函式一樣,但是它能被實現為一個巨集。putchar 函式相當於 putc(c,stdout),它把單個字元寫到標準輸出。
注意: putchar 和 getchar 都是把字元當做 int 型別而不是 char 型別來使用的, 理由上面有講。用一個例子演示一下 fgetc 和 fputc 函式的用法:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int buf;
FILE *file = fopen("linuxidc.txt","r");
if(file == NULL){
printf("fail to open the file!\n");
exit(1);
}
while((buf = fgetc(file)) != EOF){
fputc(buf,stdout);
}
fclose(file);
exit(0);
}
輸出:
hello,my name is Linux公社www.linuxidc.com
2018.10.28
這段程式碼開啟檔案 hello.txt,並使用 fgetc 函式將檔案中的內容一個位元組一個位元組地取出,同時使用 fputc 函式將該位元組寫到標準輸出流中,直到檔案結束。
九、fgets 和 gets 函式
fgets 函式從輸入檔案流 stream 裡讀取一個字串,並存放到緩衝區中,一次��取的字元個數有限制。
#include <stdio.h>
char *fgets(char *s,int n,FILE *stream);
char *gets(char *s);
fgets 函式把讀到的字元寫到 s 指向的字串裡,直到:
1)遇到換行符,則停止讀入字元,並將遇到的換行符一起傳遞給接收字串,再加上一個表示結尾的空位元組 \0;
2)已經傳輸了 n-1 個字元,則加上一個空位元組 \0 結尾後,停止讀入字元;
3)到達檔案尾(EOF)。
當成功呼叫函式時,fgets 返回一個指向字串 s 的指標。如果檔案流已經到達檔案尾,fgets 會設定這個檔案流的 EOF 標識並返回一個空指標;如果呼叫函數出錯,則 fgets 返回一個空指標並設定 errno 以指出錯誤的型別。
gets 函式類似於 fgets,只不過它直接從標準輸入(stdin)讀取資料並丟棄了遇到的換行符,它在接收字串的尾部加上一個 null 位元組、另外,需要注意的是, gets 函式並沒有對傳輸字元的個數做限制,所以它可能會溢位自己的傳輸緩衝區 。因此,一般來說,推薦使用 fgets 函式來替代 gets 函式。
Linux公社的RSS地址 : ofollow,noindex" target="_blank">https://www.linuxidc.com/rssFeed.aspx
本文永久更新連結地址: https://www.linuxidc.com/Linux/2018-10/155051.htm