一、標準 IO 的起源與概念
標準 IO(Standard Input/Output)是由 Dennis Ritchie 在 1975 年設計的一套 IO 庫,后來成為 C 語言的標準組成部分,并被 ANSI C 所采納。它是對底層文件 IO 的封裝,提供了更便捷、可移植的文件操作接口。
核心特點:
- 設備抽象:將輸入輸出設備抽象為文件操作
- 標準輸入設備:默認是鍵盤(
/dev/input
) - 標準輸出設備:默認是顯示器
- 標準輸入設備:默認是鍵盤(
- 跨平臺性:任何支持標準 C 的系統都可使用
- 緩沖機制:在用戶程序和文件 IO 之間加入緩沖區,減少系統調用次數,提高效率
在 Linux 系統中,一切皆文件,IO 操作本質上都是對文件的操作。標準 IO 作為 C 語言標準庫的一部分,為文件操作提供了統一接口。
二、文件的基本概念
1. 文件的作用
文件是 Linux 系統中存儲數據(包括普通數據和指令)的基本單位,是數據持久化的載體。
2. Linux 文件類型(7 種)
d
:目錄文件-
:普通文件l
:鏈接文件(link)p
:管道文件(pipe)s
:套接字文件(socket)c
:字符設備文件b
:塊設備文件
可通過ls -l
命令查看文件類型,第一個字符即表示文件類型。
3. 文件內容分類
- 文本文件:由 ASCII 字符組成,可直接閱讀
- 二進制文件:由二進制數據組成,通常需要特定程序解析
三、IO 的分類
1. 標準 IO
- 是 ANSI C 設計的一組封裝了文件 IO 的庫函數
- 頭文件:
stdio.h
(位于/usr/include/stdio.h
) - 實現方式:
stdio.h
→stdio.c
→libc.so
(動態庫,位于/usr/lib
) - 特點:帶緩沖機制,可移植性好
2. 文件 IO
- 屬于系統調用,是底層操作接口
- 特點:無緩沖,直接與內核交互,效率高但使用復雜
四、頭文件引用方式
-
#include <stdio.h>
:引用系統庫函數頭文件- 搜索路徑:系統默認路徑(如
/usr/include/
)
- 搜索路徑:系統默認路徑(如
-
#include "xxx.h"
:引用用戶自定義頭文件- 搜索路徑:當前目錄
五、流(Stream)的概念
流是數據從文件流入和流出所體現的字節序列,在標準 IO 中用FILE*
指針表示(稱為流對象或文件流指針)。
1. 流的分類
- 二進制流:由二進制數據組成的流
- 文本流:由 ASCII 碼數據組成的流
2. 標準流
C 語言默認打開三個標準流:
stdin
:標準輸入流(對應鍵盤)stdout
:標準輸出流(對應顯示器)stderr
:標準錯誤流(對應顯示器)
六、緩沖區機制
標準 IO 的一大特點是采用緩沖機制,目的是減少系統調用,提高效率。
1. 緩沖區類型
-
行緩沖(1KB)
- 應用場景:主要用于終端交互(如
stdout
) - 刷新條件:遇到
\n
、緩沖區滿、程序正常結束、fflush()
強制刷新
- 應用場景:主要用于終端交互(如
-
全緩沖(4KB)
- 應用場景:主要用于普通文件操作
- 刷新條件:緩沖區滿、程序正常結束、
fflush()
強制刷新
-
無緩沖(0KB)
- 應用場景:主要用于錯誤處理(如
stderr
) - 特點:數據直接輸出,不經過緩沖
- 應用場景:主要用于錯誤處理(如
2. 緩沖區操作函數
fflush(FILE *stream)
:強制刷新緩沖區
七、標準 IO 常用函數
1. 文件打開與關閉
-
fopen()
:打開文件并建立流
?FILE *fopen(const char *path, const char *mode);
mode
參數:r
:只讀(文件不存在則報錯)r+
:讀寫(文件不存在則報錯)w
:只寫(文件不存在則創建,存在則清空)w+
:讀寫(文件不存在則創建,存在則清空)a
:追加寫(文件不存在則創建)a+
:追加讀寫(文件不存在則創建)
-
fclose()
:關閉文件流int fclose(FILE *stream);
2. 字符操作函數
fgetc(FILE *stream)
:從流中讀取一個字符fputc(int c, FILE *stream)
:向流中寫入一個字符getchar()
:從標準輸入讀取一個字符(fgetc(stdin)
的宏定義)putchar(int c)
:向標準輸出寫入一個字符(fputc(c, stdout)
的宏定義)
示例:實現簡單的輸入輸出循環
while(1)fputc(fgetc(stdin), stdout);
3. 行操作函數
-
fgets(char *s, int size, FILE *stream)
:從流中讀取一行數據
?char *fgets(char *s, int size, FILE *stream);
- 最多讀取
size-1
個字符,自動添加\0
- 遇到
\n
會停止讀取,且\n
會被包含在結果中
- 最多讀取
-
fputs(const char *s, FILE *stream)
:向流中寫入一行數據int fputs(const char *s, FILE *stream);
-
gets()
/puts()
:與fgets()
/fputs()
類似,但gets()
沒有緩沖區大小限制,存在安全隱患
4. 塊操作函數(二進制操作)
-
fread()
:從流中讀取指定大小的數據塊size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
-
fwrite()
:向流中寫入指定大小的數據塊size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
示例:結構體讀寫
struct person {char name[20];int age;char sex;char addr[100];
};struct person p = {"Tom", 20, 'M', "Beijing"};
fwrite(&p, sizeof(struct person), 1, fp);
5. 文件定位函數
-
fseek()
:移動文件指針
?int fseek(FILE *stream, long offset, int whence);
whence
參數:SEEK_SET
:從文件開頭SEEK_CUR
:從當前位置SEEK_END
:從文件末尾
-
ftell()
:獲取當前文件指針位置long ftell(FILE *stream);
-
rewind()
:將文件指針移到開頭(等效于fseek(stream, 0L, SEEK_SET)
)void rewind(FILE *stream);
6. 其他常用函數
printf()
/scanf()
:格式化輸入輸出sprintf()
:將格式化數據寫入字符串feof()
:判斷文件是否到達末尾ferror()
:檢測流是否出錯clearerr()
:清除流出錯標記
八、標準 IO 操作流程
- 打開文件:使用
fopen()
獲取文件流指針(FILE*
) - 讀寫操作:根據需求選擇合適的 IO 函數(字符、行、塊操作)
- 關閉文件:使用
fclose()
關閉文件流,釋放資源
九、實踐練習
1. 實現簡易cat
程序
#include <stdio.h>int main(int argc, char *argv[]) {FILE *fp;int c;if (argc != 2) {fprintf(stderr, "Usage: %s filename\n", argv[0]);return 1;}fp = fopen(argv[1], "r");if (fp == NULL) {fprintf(stderr, "Cannot open file %s\n", argv[1]);return 1;}while ((c = fgetc(fp)) != EOF) {fputc(c, stdout);}fclose(fp);return 0;
}
2. 實現文件拷貝功能
#include <stdio.h>
#include <stdlib.h>#define BUFFER_SIZE 1024int main(int argc, char *argv[]) {FILE *src, *dest;char buffer[BUFFER_SIZE];size_t n;if (argc != 3) {fprintf(stderr, "Usage: %s source destination\n", argv[0]);return 1;}src = fopen(argv[1], "rb");if (src == NULL) {fprintf(stderr, "Cannot open source file %s\n", argv[1]);return 1;}dest = fopen(argv[2], "wb");if (dest == NULL) {fprintf(stderr, "Cannot open destination file %s\n", argv[2]);fclose(src);return 1;}while ((n = fread(buffer, 1, BUFFER_SIZE, src)) > 0) {fwrite(buffer, 1, n, dest);}fclose(src);fclose(dest);return 0;
}
十、man 手冊的使用
man 手冊是查詢函數和命令的重要工具,分為不同章節:
man 1 xxx
:查看命令幫助man 2 xxx
:查看系統調用函數man 3 xxx
:查看標準庫函數
例如:
man 3 fopen
:查看 fopen 函數的詳細說明man 3 printf
:查看 printf 函數的用法
總結
標準 IO 是 C 語言中處理文件操作的重要接口,通過緩沖機制提高了 IO 效率,同時提供了豐富的函數族滿足不同場景的需求。掌握標準 IO 的使用,對于 C 語言程序開發至關重要。從基本的字符讀寫到復雜的文件定位,標準 IO 都提供了簡潔而強大的解決方案,是每個 C 程序員必須掌握的基礎知識。