在上一篇Linux編程學習筆記 | Linux IO學習[1] - 文件IO中,我總結了Linux下的文件IO。文件IO是偏底層的IO操做,在平時的平常工做中,使用文件IO的頻率仍是比較低的。咱們天天使用的 printf()
就不是文件IO,而是另外一類IO - 標準IO。在這篇文章中,我將介紹Linux下的標準IO並經過實例來講明如何使用它們。linux
要使用標準IO庫,須要包含頭文件 <stdio.h>
。該庫爲用戶建立了一個鏈接底層系統調用的通用接口,它是ANSI C標準制定的庫,所以具備可移植性(文件IO是基於Unix的POSIX標準,不可移植到Windows)。同文件IO相似,它須要先打開一個文件以創建一個訪問途徑,文件IO的訪問途徑是經過文件描述符,標準IO的訪問途徑是流(stream),它被實現爲指向結構FILE的指針。git
同文件IO相似,在程序啓動時也有3個默認的文件流:github
標準流 | 變量或宏 | 說明 |
---|---|---|
0 | stdin | 標準輸入 |
1 | stdout | 標準輸出 |
2 | stderr | 標準錯誤輸出 |
標準IO的函數相對文件IO來講要多不少,我這裏主要介紹13個標準IO函數。編程
fopen()
和文件IO中的 open()
相似,用於打開文件流,函數說明以下:segmentfault
FILE *fopen(const char *restrict pathname, const char *restrict mode); args: const char *restrict pathname: 文件的路徑 const char *restrict mode : 文件打開的模式 return: 返回指向文件的指針,指針不爲NULL是成功,指針爲NULL是失敗
文件打開的模式有如下6種:函數
1. "r" or "rb" : 以只讀形式打開文(文件必須存在) 2. "w" or "wb" : 以寫方式打開文件並將文件長度截爲0或建立一個供寫的文件 3. "a" or "ab" : 以寫方式打開文件並將內容寫到文件末或建立一個文件 4. "r+" or "rb+" or "r+b" : 以更新的方式(讀/寫)打開文件(文件必須存在) 5. "w+" or "wb+" or "w+b" : 以更新的方式(讀/寫)打開文件並將文件長度截爲0或建立一個文件 6. "a+" or "ab+" or "a+b" : 以更新的方式(讀/寫)打開文件並將更新內容寫到文件末或建立一個文件
fopen()
和 open()
不一樣, fopen()
並不能在建立文件時改變其訪問權限。學習
fclose()
和文件IO中的 close()
相似,用於關閉文件流,函數說明以下:指針
int fclose(FILE *stream); args: FILE *stream: 指向被關閉文件的指針 return: 關閉文件成功返回0,關閉文件失敗返回而EOF
咱們來看第一個例子,文件的打開和關閉:rest
#include <stdio.h> int main(int argc, char *argv[]) { FILE *fp; //fp = fopen("stdio.log", "r+"); fp = fopen("stdio.log", "w+"); if (fp == NULL) { printf("File create fail...\n"); return -1; } else { printf("File create success...\n"); } fclose(fp); return 0; }
運行結果:
新建一個叫 stdio.log
的文件,並輸出File create success...
若是咱們註釋掉第8行,去掉第7行的註釋,那麼將輸出 File create fail...
。code
fseek()
和文件IO中的 lseek()
相似,用於修改文件流讀寫的偏移量,函數說明以下:
int fseek(FILE *stream, long offset, int whence); args: FILE *stream: 指向文件的文件指針 long offset : 偏移量移動的距離 int whence : 偏移量的基址 - SEEK_SET 文件開始處 - SEEK_CUR 文件當前位置 - SEEK_END 文件結束處 return: 修改偏移量成功返回0, 修改偏移量失敗返回-1
當 whence
是 SEEK_CUR
或 SEEK_END
時, offset
可正負。
fwrite()
和文件IO中的 write()
相似,函數說明以下:
size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream); args: const void *restrict ptr: 寫入數據在內存空間存儲的地址 size_t size : 單個元素的大小 size_t nitems : 寫入數據元素的個數 FILE *restrict stream : 指向寫入文件的文件指針 return: 實際寫入的元素個數,非負整數是成功,-1是失敗
fputs()
將字符串(不包括 `\0` )寫入文件,函數說明以下:
int fputs(const char *restrict s, FILE *restrict stream); args: const char *restrict s: 寫入的字符串 FILE *restrict stream : 指向寫入文件的文件指針 return: 寫入文件的狀態,非負整數是成功,EOF是失敗
puts()
將字符串(不包括 `\0` )寫入 stdout
,並在行末添加一個換行符,函數說明以下:
int puts(const char *s); args: const char *s: 寫入的字符串 return: 寫出到stdio的狀態,非負整數是成功,EOF是失敗
fputc()
將一個字符寫入文件,函數說明以下:
int fputc(int c, FILE *stream); args: int char : 要寫入的字符 FILE *stream: 指向寫入文件的文件指針 return: 若是沒有錯誤,返回寫入的字符,不然返回EOF
putc()
和 fputc()
基本同樣,只不過 putc()
是用宏實現而 fputc
是用函數實現。
int putc(int c, FILE *stream); args: int c : 要寫入的字符 FILE *stream: 指向寫入文件的文件指針 return: 若是沒有錯誤,返回寫入的字符,不然返回EOF
咱們經過例子來看看上面這幾個函數的使用方法:
#include <stdio.h> int main(int argc, char *argv[]) { FILE *fp; fp = fopen("stdio.log", "w+"); if (fp == NULL) { printf("File create fail...\n"); return -1; } else { printf("File create success...\n"); } /* fwrite() function */ char buffer_1[] = "This is fwrite DEMO..."; size_t wr_size = 0; wr_size = fwrite(buffer_1, 1, sizeof(buffer_1), fp); printf("wr_size = %d\n", wr_size); /* fputs() function */ char buffer_2[] = "\nThis is fputs DEMO...\n"; int fputs_status = 0; fputs_status = fputs(buffer_2, fp); printf("fputs_status = %d\n", wr_size); /* puts function */ char buffer_3[] = "This is puts DEMO..."; puts(buffer_3); /* fputc function */ char buffer_4[] = "This is fputc DEMO...\n"; int ret; for (int i = 0; i < sizeof(buffer_4); i++) { ret = fputc(buffer_4[i], fp); printf("%c", ret); } /* putc function */ char buffer_5[] = "This is putc DEMO...\n"; for (int i = 0; i < sizeof(buffer_5); i++) { ret = fputc(buffer_5[i], fp); printf("%c", ret); } fclose(fp); return 0; }
運行結果:
在生成的 std_io.log
文件中會輸出如下內容,其中 @^
就是 `\0`
This is fwrite DEMO...^@ This is fputs DEMO... This is fputc DEMO... ^@This is putc DEMO... ^@
注意 fputs
函數並無輸出 `\0` 。在終端會輸出:
File create success... wr_size = 23 fputs_status = 23 This is puts DEMO... This is fputc DEMO... This is putc DEMO...
puts
函數直接將字符串輸出到 stdio
。
fread()
和文件IO中的 read()
相似,函數說明以下:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); args: void *ptr : 讀取數據存儲的內存空間的地址 size_t size : 單個元素的大小 size_t nmemb: 讀取數據元素的個數 FILE *stream: 指向讀取文件的文件指針 return: 實際讀取的元素個數,非負整數是成功,-1是失敗
fgets()
用於讀取文件中的字符串,而後將其存儲到內存空間,函數說明以下:
char *fgets(char *restrict s, int n, FILE *restrict stream); args: char *restrict s : 讀取後字符串存儲的內存空間地址 int n : 最大讀取字符數 FILE *restrict stream: 指向讀取文件的文件指針 return: 若是讀取沒有錯誤且沒有讀入EOF,返回寫入的字符串 若是讀取沒有錯誤但讀入EOF,返回NULL指針 若是讀取出現錯誤,返回NULL指針
這裏須要注意下該函數將在什麼時候中止讀取:
若是讀取的字符數量達到 n - 1
,或讀取了換行符,或讀取了字符串結束符,只要有一個知足則該函數會中止繼續讀取。
gets()
從 stdin
中讀取字符串並存放在內存中,函數說明以下:
char *gets(char *s); args: char *s: 讀取後字符串存儲的內存空間地址 return: 若是讀取沒有錯誤且沒有讀入EOF,返回讀取的字符串 若是讀取沒有錯誤但讀入EOF,返回NULL指針 若是讀取出現錯誤,返回NULL指針
讀取操做將在讀入換行符或EOF後結束。
fgetc()
從一個文件讀取一個字符,函數說明以下:
int fgetc(FILE *stream); args: FILE *stream: 指向讀取文件的文件指針 return: 若是讀取沒有錯誤且沒有讀入EOF,返回讀取的字符 若是讀取沒有錯誤但讀入EOF,返回EOF 若是讀取出現錯誤,返回EOF
getc()
和 fgetc()
基本同樣,只不過 getc()
是用宏實現而 fgetc()
是用函數實現。
int getc(FILE *stream); args: FILE *stream: 指向讀取文件的文件指針 return: 若是讀取沒有錯誤且沒有讀入EOF,返回讀取的字符 若是讀取沒有錯誤但讀入EOF,返回EOF 若是讀取出現錯誤,返回EOF
說完了讀操做的函數,咱們也經過一個例子來看看如何使用這些函數:
#include <stdio.h> int main(int argc, char *argv[]) { FILE *fp; fp = fopen("stdio.log", "r+"); if (fp == NULL) { printf("File open fail...\n"); return -1; } else { printf("File open success...\n"); } /* fread() function */ char buffer_1[50]; size_t rd_size = 0; rd_size = fread(buffer_1, 1, 24, fp); printf("rd_size = %d\n", rd_size); printf("fread get: %s\n", buffer_1); /* fgets() function */ char buffer_2[50]; char *fgets_status; fgets_status = fgets(buffer_2, 23, fp); printf("fgets_status = %s", fgets_status); printf("fgets get: %s", buffer_2); /* gets function */ char buffer_3[50]; gets(buffer_3); printf("gets get: %s", buffer_3); /* fgetc function */ int ret; while ((ret = fgetc(fp)) != EOF) printf("%c", ret); fclose(fp); return 0; }
在編譯過程當中,編譯器會警告:
warning: implicit declaration of function ‘gets’ [-Wimplicit-function-declaration] gets(buffer_3); ^~~~ /tmp/cc3YWk3i.o: In function `main': fread_get.c:(.text+0x11d): warning: the `gets' function is dangerous and should not be used.
由於 gets()
函數過於危險,在C11中的 <stdio.h>
已經再也不包含 gets()
函數,所以會出現這個警告。
運行結果:
要讀取的文件是以前寫函數生成的 stdio.log
文件,終端輸出以下。
File open success... rd_size = 24 fread get: This is fwrite DEMO... fgets_status = This is fputs DEMO... fgets get: This is fputs DEMO... test gets get: testThis is fputc DEMO... This is putc DEMO...
這篇文章主要介紹瞭如何使用幾個基本的標準IO函數對文件進行操做,相對於文件IO而言,標準IO使用更方便,而且支持跨平臺使用。同時在傳輸大文件時,標準IO也不比文件IO慢。文中出現的代碼均可在個人github上找到。
若是以爲本文對你有幫助,請多多點贊支持,謝謝!