目錄
- 前言
- 一、核心概念:
- 二、關鍵操作步驟:
- 三、為什么需要文件IO?
- 四、常見類型:
- 五、標準IO源碼
- 六、筆試真題和練習
- 1.
- 代碼實現1
- 代碼實現2
- 2.
- 代碼實現
- 3.
- 代碼實現
- 4.
- 代碼實現
- 5.
- 代碼實現
- 七、總結
前言
文件IO(文件輸入/輸出) 是指計算機程序與外部存儲設備(如硬盤、SSD、U盤等)上的文件進行數據交換的過程。簡單來說,就是程序從文件中讀取數據(Input)或將數據寫入文件(Output)。文件IO這個板塊我們會學習很多個接口,所以說我們長時間不用就會忘記,一定要會用man指令查手冊,必須要回看得懂源碼。
一、核心概念:
文件(File):
存儲在持久化介質(如硬盤)上的數據集合,具有名稱和路徑。可以是文本、圖片、音頻、程序等任何形式。
輸入(Input):
程序從文件讀取數據到內存(如變量、數據結構)。例如:讀取配置文件、加載圖片數據。
輸出(Output):
程序將內存中的數據寫入文件。例如:保存用戶設置、導出計算結果。
二、關鍵操作步驟:
打開文件(Open):
建立程序與文件的連接,指定操作模式(讀、寫、追加等)。
讀寫數據(Read/Write):
**讀:**將文件內容傳輸到程序內存。
**寫:**將程序內存中的數據寫入文件。
關閉文件(Close):
釋放資源,確保數據完整保存(重要!未關閉可能導致數據丟失)。
三、為什么需要文件IO?
**持久化存儲:**內存數據在程序關閉后消失,文件可長期保存。
**數據共享:**不同程序或用戶可通過文件交換數據。
**處理大數據:**內存有限,文件可分批讀取/寫入海量數據。
四、常見類型:
**文本文件IO:**處理字符數據(如 .txt, .csv),需注意編碼(UTF-8等)。
**二進制文件IO:**直接處理字節(如圖片 .jpg、可執行程序),無編碼轉換。
五、標準IO源碼
因為標準IO的接口都是在Linux的man手冊的第3長,在Linux系統輸入man 3 fopen,man 3 fread,等常用的函數接口
六、筆試真題和練習
1.
代碼實現1
這次我用的是fopen打開文件,fclose關閉文件,fgetc一個一個字符讀取文件中的內容,所以光標會自己向后偏移。
#include <stdio.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{if(argc != 2){printf("argument is invailed!\n");exit(1);}FILE* file = fopen(argv[1], "rb");if(!file){printf("file open failed!\n");exit(1);}int cnt = 0;while(1){if(fgetc(file) == EOF){break;}cnt++;}printf("file %s size is %d byte\n",argv[1],cnt);fclose(file);return 0;
}
代碼實現2
這個代碼是利用fseek函數將文件的光標移動到文件末尾,然后用ftell函數記錄當前光標位置到文件開頭的偏移值。
#include <stdio.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{if(argc != 2){printf("argument is invailed!\n");exit(1);}FILE* file = fopen(argv[1], "rb");if(!file){printf("file open failed!\n");exit(1);}fseek(file,0,SEEK_END);printf("file %s size is %ld byte\n",argv[1], ftell(file));fclose(file);return 0;
}
2.
### 代碼實現
用到了fread和fwrite接口,定義一個緩沖區(buffer),記錄讀寫次數,每次讀寫前把緩沖區清空。
代碼實現
/** Copyright (c)* * date: 2025-7-22* * author: Charles* * function name : copyFile** function: 把一個文件的所有內容拷貝到另一個文件*/#include <stdio.h>
#include <stdlib.h>
#include <strings.h>#define BUFFERSIZE 512int main(int argc, char const *argv[])
{long file_src_size = 0; //源文件的總字節數long remainder = 0; //總字節數與緩沖區字節數的余數int cnt = 0; //用源文件總字節數除以緩沖區字節數,也就是再循環里的讀寫次數long file_des_size = 0; //目標文件的總字節數char buffer[BUFFERSIZE] = {0}; //緩沖區if(argc != 3){printf("argument is invailed!\n");exit(1);}FILE* file_src = fopen(argv[1], "rb");FILE* file_des = fopen(argv[2], "wb");if(!file_src || !file_des){printf("file open failed!\n");exit(1);}fseek(file_src, 0, SEEK_END);file_src_size = ftell(file_src);fseek(file_src, 0, SEEK_SET);printf("file_src [%s] size is [%ld] byte\n",argv[1], file_src_size);cnt = file_src_size / BUFFERSIZE;remainder = file_src_size % BUFFERSIZE;while(cnt--){memset(buffer, 0, BUFFERSIZE);size_t n = fread(buffer, 1, BUFFERSIZE, file_src);fwrite(buffer, 1, n, file_des);}if(remainder){memset(buffer, 0, BUFFERSIZE);size_t n = fread(buffer, 1, remainder, file_src);fwrite(buffer, 1, n, file_des);}fseek(file_des, 0, SEEK_END);file_des_size = ftell(file_des);fseek(file_des, 0, SEEK_SET);printf("file_des [%s] size is [%ld] byte\n",argv[2], file_des_size);fclose(file_src);fclose(file_des);return 0;
}
3.
代碼實現
在一個循環里用open函數,記錄直到函數返回值為-1為止。
/** Copyright (c)* * date: 2025-7-22* * author: Charles* * function name : FileOpenCount** function: 測試文件打開次數*/#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#define BUFFERSIZE 512int main(int argc, char const *argv[])
{if(argv != 2){printf("argument is invailed!\n");exit(1);}int cnt = 0;while(open(argv[1], O_RDWR) != -1){cnt++;}printf("file can be open %d count!\n",cnt + 3);//為什么加3,因為在打開這個文件的時候系統會自動打開標準輸入,標準輸出,錯誤這個文件return 0;
}
4.
像這個獲取時間的api,都是系統IO。
代碼實現
/** Copyright (c)* * date: 2025-7-23* * author: Charles* * function name : time_log.c** function: 每秒鐘打印當前日期到log.txt文件中*/#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>int main(int argc, char const *argv[])
{while(1){time_t t = time(NULL);struct tm *tm_info = localtime(&t);char day[8][20] = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};char buffer[256];snprintf(buffer,sizeof(buffer),"%d年%02d月%02d日 %s %02d:%02d:%02d",tm_info->tm_year + 1900,tm_info->tm_mon + 1,tm_info->tm_mday,day[tm_info->tm_wday],tm_info->tm_hour,tm_info->tm_min,tm_info->tm_sec);FILE* file = fopen("./log.txt","a");if(!file){perror("fopen");exit(-1);}fputs(buffer, file);fclose(file);sleep(1);}return 0;
}
5.
這題可以看看bmp的文件存儲格式圖。然后要注意的是系統默認會有一個對齊機制,就比如說bmp_header這個結構體如果取消對齊大小為14字節,如果不取消大小就為16字節。
代碼實現
/** Copyright (c)* * date: 2025-7-23* * author: Charles* * function name : getBMP.c** function: 獲取bmp圖片大小,像素寬度和高度信息*/#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>#pragma pack(1) //取消字節對齊struct bmp_header{char s[2];int size;short a;short b;int c;
};struct bmp_info{int a;int width;int heigth;short a1;short b1;int off1;int off2;int off3;int off4;int off5;int off6;
};#pragma pack(0)int main(int argc, char const *argv[])
{// int bmp_size = 0; //bmp圖片的總字節數// int bmp_width = 0; //bmp圖片的像素寬度// int bmp_heigth = 0; //bmp圖片的像素高度if(argc != 2){ //參數錯誤處理printf("argument fail! \n");exit(-1);}int bmp_fd = open(argv[1], O_RDWR);if(bmp_fd == -1){printf("open file : %s fail\n", argv[1]);exit(-1);}// char header[2] = {0};// read(bmp_fd, header, 2);struct bmp_header bh;struct bmp_info bi;read(bmp_fd, &bh, 14);read(bmp_fd, &bi, 40);if(bh.s[0] != 'B' || bh.s[1] != 'M'){ //判斷寫入的兩個字符是否是BM開頭的printf("open file not bmp\n");exit(-1);}printf("bmp size is %d byte\n", bh.size);printf("bmp width is %d px\n", bi.width);printf("bmp heigth is %d py\n", bi.heigth);// lseek(bmp_fd, 2, SEEK_SET);// read(bmp_fd, &bmp_size, 4);// printf("bmp size is %d byte\n", bmp_size);// lseek(bmp_fd, 18, SEEK_SET);// read(bmp_fd, &bmp_width, 4);// printf("bmp width is %d byte\n", bmp_width);// lseek(bmp_fd, 18 + 4, SEEK_SET);// read(bmp_fd, &bmp_heigth, 4);// printf("bmp heigth is %d byte\n", bmp_heigth);close(bmp_fd);return 0;
}
七、總結
文件IO是程序與外部存儲交互的橋梁,通過 讀取(Input) 和 寫入(Output) 實現數據的持久化和重用,是幾乎所有軟件的基礎功能。不同編程語言有各自的API(如C的 fopen/fread、Java的 FileInputStream、Python的 open()),但核心邏輯一致。
希望各位靚仔靚女點贊,收藏,關注多多支持,我們共同進步,后續我會更新更多的面試真題,你們的支持將是我前進最大的動力。