C指標程式設計之道(四)- 指向檔案型別的指標
阿新 • • 發佈:2019-01-09
檔案結構體和檔案指標
// 在c語言中,對檔案的操作一般是用庫函式來實現的
// ANSI(美國國家標準學會)規定了標準的輸入輸出函式,用他們對檔案進行讀寫以規範和提高程式設計效率
// 每個被使用的檔案在記憶體中都會開闢一個區,用來存放檔案的有關資訊,而這些資訊都儲存在檔案結構體FILE中
// FILE 用於訪問一個流,若同時激活了幾個流,那麼每個流都有一個相應的FILE與之相關聯
// 為了在流上執行一些操作,需要呼叫一些合適的庫函式,並傳遞一個與流相關的FILE引數
// C語言定義了三個特別的檔案指標常數,標準輸入、標準輸出、標準錯誤,流的名稱分別為stdin、stdout、stderr,他們都是一個指向FILE的指標
typedef struct {
short level; //緩衝區,“滿”或“空”的程度
unsigned flags; //檔案狀態的標誌
char fd; //檔案描述符
unsigned char hold; //如無緩衝區不讀取字元
short basize; //緩衝區的大小
unsigned char *buffer; //資料緩衝區的位置
unsigned char *curp; //指標當前的指向
unsigned istemp; //臨時檔案,指示器
short token; //用於有效性的檢驗
} FILE;
// 有了結構體FILE,我們就可以用它來定義FILE型別的變數,用來存放待處理檔案的資訊
FILE arrayf[10];
// 我們還可以定義一個檔案型別的指標,即指向檔案的指標
FILE *fp;
標準流的I/O函式
資料型別 輸入 輸出 描述
----------------------------------------------------------
字元 getchar putchar 讀取(寫入)單個字元
文字行 fgets fputs 文字行未格式化的輸入(輸出)
scanf printf 文字行格式化的輸入(輸出)
----------------------------------------------------------
# include <stdio.h>
int main() {
char ch;
//char s[10];
//printf("請輸入單個字元: ");
//ch = getchar();
//putchar(ch);
//printf("請輸入單個字串: ");
//fgets(s, sizeof(s)/sizeof(char), stdin);
//fputs(s, stdout);
printf("請輸入一個字元: ");
scanf(" %c", &ch);
printf("%c\n", ch);
return 0;
}
常見檔案操作函式
開啟與關閉:fopen,fclose
讀寫字元:fgetc,fputc
讀寫字串:fgets,fputs
按資料塊讀寫:fread,fwrite
格式化讀寫:fscanf,fprintf
檔案定位:fseek,rewind,ftell
判斷檔案是否結尾:feof
fopen
函式原型:FILE * fopen(char const *name, char *mode)
相關解釋:用於開啟一個特定的檔案,並把一個流和這個檔案相關聯
fclose
函式原型:int fclose(FILE *fp)
相關解釋:用於關閉一個特定的檔案,所謂關閉就是使檔案指標變數不再指向該檔案,之後不能再通過該指標變數對檔案進行讀寫操作。
對於輸出流,fclose函式在檔案關閉之前會重新整理緩衝區,這裡需要注意,在程式結束之前,我們一定要關閉所有已經開啟
的檔案,否則會產生資料的丟失。比如,在向檔案寫資料時,總是先將資料存到緩衝區,待緩衝區滿後才將資料寫入檔案,
若緩衝區未滿時程式結束,則緩衝區的資料將會丟失。使用fclose函式關閉檔案,他會將緩衝區的資料寫入檔案,然後才
釋放檔案指標,從而可以避免檔案的丟失。fclose有一個int型別的返回值,檔案關閉成功時返回0,失敗時返回EOF(-1)
fgetc
函式原型:int fgetc(FILE *fp)
相關解釋:從指定的檔案讀取一個字元,該檔案必須是以只讀或讀寫的方式開啟的。fgetc從fp中讀出一個字元並返回這個字元的ASCII碼,
若讀到檔案結束符,則函式返回檔案結束標誌。
fputc
函式原型:int fputc(int c, FILE *fp)
相關解釋:把一個字元寫到檔案中,若寫入成功則返回寫入字元的ASCII碼,若失敗則返回EOF。
fgets
函式原型:char * fgets(char *s, int n, FILE *fp)
相關解釋:從fp指向的檔案中讀取字元,當讀到回車符、檔案末尾、或滿n-1個字元時 就在字串末尾加上'\0',把他們存放到字元陣列s
中後函式返回,若在任何字元讀取錢就達到了檔案末尾,fgets就返回NULL指標(用於檢查是否到達檔案尾),否則返回s的首地址
fputs
函式原型:int * fputs(const char *s, FILE *fp)
相關解釋:向指定的檔案中輸入一個字串,若寫入成功則返回0,否則返回EOF。
fread
函式原型:unsigned fread(void *s, unsigned size, unsigned count, FILE *fp)
相關解釋:從fp指向的檔案中讀取二進位制資料塊到s陣列,函式有四個引數
第一個引數s是從檔案中讀物資料的存放地址,
第二個引數size是要讀的位元組數的大小,
第三個引數count是要讀取多少個size位元組的資料,即最多允許讀取的資料塊的個數,
第四個引數fp為指向要讀取的檔案的指標
fwrite
函式原型:unsigned fwrite(void *s, unsigned size, unsigned count, FILE *fp)
相關解釋:將二進位制資料塊s寫入到fp指向的檔案,函式有四個引數
第一個引數s是要向檔案寫入的資料的存放地址,
第二個引數size是要寫的位元組數的大小,
第三個引數count是要寫多少個size位元組的資料,即最多允許寫的資料塊的個數,
第四個引數fp為指向要寫入的檔案的指標
返回值1表示寫入成功,0表示寫入失敗
fprintf
函式原型:int fprintf(FILE *fp, const char *format, ...)
相關解釋:fprintf函式與printf函式相似,都是格式化輸出函式,不同的是printf輸出的位置是終端,而fprintf輸出的位置是檔案
fscanf
函式原型:int fscanf(FILE *fp, const char *format, ...)
相關解釋:fscanf函式與scanf函式相似,都是格式化輸入函式,不同的是scanf輸入的位置是終端,而fscanf輸入的位置是檔案
[注] 用fprintf和fscanf對檔案進行讀寫,在讀入時需要將ASCII碼轉化成二進位制數,在輸出時又需將二進位制數轉化為
字元,時間開銷會比較大,因此交換資料比較頻繁的情況下最好不要用這兩個函式。
fseek
函式原型:int fseek(FILE *fp, long offset, int from)
相關解釋:FILE中有一個指向當前位置的位置指標,如果順序讀寫檔案,每次讀取一個字元,讀完一個字元之後,位置指標自動移動到下
一個字元。此外我們可以通過函式改變這個指標的位置實現對檔案的隨機讀寫,這樣的函式就是檔案定位函式,fseek就是這
樣一個檔案定位函式,該函式可以改變檔案的位置指標所指向位置,也就是改變下一個讀取或寫入位置。fseek有三個引數,
第一個引數fp指向要操作檔案的指標;
第二個引數offset是位移量,它是以起始點為基點,向前移動的位元組數;
第三個引數from為開始定位的起始點。
其中from的取值可以有如下三個(offset可以為負數):
0 SEEK_SET 定位起始點為檔案的開始
1 SEEK_CUR 定位起始點為檔案位置指標當前的位置
2 SEEK_END 定位起始點為檔案的末尾
rewind
函式原型:void rewind(FILE *fp)
相關解釋:rewind為檔案定位函式,作用是使檔案的位置指標重新返回到檔案的開頭,它同時清除流的錯誤提示標誌。
ftell
函式原型:long ftell(FILE *fp)
相關解釋:得到檔案指標的當前位置,用相對檔案開頭的位移量來表示,出錯時返回-1L,這個函式可以讓你儲存一個檔案的當前位置,這樣
可以在將來再次返回到這個位置。在二進位制檔案中,返回值就是當前位置到檔案頭的位元組數;在文字檔案中,不一定是當前位置到
檔案頭的位元組數,因為有的系統會對行末字元進行轉換。
feof
函式原型:int feof(FILE *fp)
相關解釋:用於判斷檔案是否結束,當檔案指標指向fp末尾時,函式返回真,否則返回0。
相關測試:
/**
* 複製檔案,一次讀取一個字元
*/
# include <stdio.h>
# include <stdlib.h>
int main() {
FILE *fpr, *fpw;
FILE * open_file(const char *, const char *);
//開啟檔案
fpr = open_file("/Users/zhangqingli/Desktop/test.txt", "r");
fpw = open_file("/Users/zhangqingli/Desktop/test2.txt", "w");
//複製原檔案內容到目標檔案,每次只讀取一個字元
char ch;
while ((ch=fgetc(fpr)) != EOF) {
printf("%c", ch);
fputc(ch, fpw);
}
//關閉檔案
fclose(fpr);
fclose(fpw);
return 0;
}
//開啟一個檔案
FILE * open_file(const char *filename, const char *mode) {
FILE * fp = fopen(filename, mode);
if (fp==NULL) {
printf("檔案開啟失敗!\n");
exit(1);
}
return fp;
}
/**
* 複製檔案,一次讀取或寫入一個字串
*/
# include <stdio.h>
# include <stdlib.h>
# define BUFFER_SIZE 10
int main() {
FILE *fpr, *fpw;
FILE * open_file(const char *, const char *);
//開啟檔案
fpr = open_file("/Users/zhangqingli/Desktop/test.txt", "r");
fpw = open_file("/Users/zhangqingli/Desktop/test2.txt", "w");
//複製原檔案內容到目標檔案,一次複製一個字串
char buffer[BUFFER_SIZE];
while ((fgets(buffer, BUFFER_SIZE, fpr))) {
printf("%s", buffer);
fputs(buffer, fpw);
}
//關閉檔案
fclose(fpr);
fclose(fpw);
return 0;
}
//開啟一個檔案
FILE * open_file(const char *filename, const char *mode) {
FILE * fp = fopen(filename, mode);
if (fp==NULL) {
printf("檔案開啟失敗!\n");
exit(1);
}
return fp;
}
/**
* 從鍵盤輸入3個關於圖書的資料,存入檔案,再從檔案中取出資料,顯示到控制檯上
*/
# include <stdio.h>
# include <stdlib.h>
# define MIX_SIZE 3
struct book_info {
char name[20];
char author[20];
} book_array[MIX_SIZE];
FILE * open_file(const char *filename, const char *mode);
int save_book_info(const char *filename);
int display_book_info(const char *filename);
int main() {
//初始化圖書資訊
printf("初始化圖書的資訊如下\n");
for (int i=0; i<MIX_SIZE; i++) {
printf("請輸入圖書名稱:");
scanf(" %s", book_array[i].name);
printf("請輸入圖書的作者:");
scanf(" %s", book_array[i].author);
}
//core
const char *filename = "/Users/zhangqingli/Desktop/test.txt";
if (save_book_info(filename)) {
display_book_info(filename);
}
return 0;
}
//開啟一個檔案
FILE * open_file(const char *filename, const char *mode) {
FILE * fp = fopen(filename, mode);
if (fp==NULL) {
printf("檔案開啟失敗!\n");
exit(1);
}
return fp;
}
//儲存圖書資訊到檔案
int save_book_info(const char *filename) {
FILE * fpw;
//開啟要寫的檔案
fpw = open_file(filename, "w");
//將圖書資訊存入待寫入檔案
for (int i=0; i<MIX_SIZE; i++) {
int status = (int) fwrite(&book_array[i], sizeof(struct book_info), 1, fpw);
if (status != 1) {
printf("檔案寫入出錯!\n");
return 0;
}
}
//關閉檔案
fclose(fpw);
return 1;
}
//從檔案讀取圖書資訊到控制檯
int display_book_info(const char *filename) {
FILE * fpr;
//開啟要讀取的檔案
fpr = open_file(filename, "r");
//將檔案中的資料讀出並顯示
for (int i=0; i<MIX_SIZE; i++) {
fread(&book_array[i], sizeof(struct book_info), 1, fpr);
printf("%s\t%s\n", book_array[i].name, book_array[i].author);
}
//檔案關閉
fclose(fpr);
return 1;
}
/**
* 計算一個檔案的大小
*/
# include <stdio.h>
# include <stdlib.h>
FILE * open_file(const char * filename, const char * mode);
long file_size(FILE * fp);
int main() {
FILE * fpr = open_file("/Users/zhangqingli/Desktop/test.txt", "r");
long length = file_size(fpr);
printf("檔案的大小為:%ld byte\n", length);
return 0;
}
//開啟檔案
FILE * open_file(const char * filename, const char * mode) {
FILE * fp;
fp = fopen(filename, mode);
if (fp==NULL) {
printf("檔案開啟失敗!\n");
exit(1);
}
return fp;
}
//計算檔案大小
long file_size(FILE * fp) {
long curpos, length;
//計算檔案大小
curpos = ftell(fp);
fseek(fp, 0L, SEEK_END);
length = ftell(fp);
//恢復檔案指標到當前位置
fseek(fp, curpos, SEEK_SET);
return length;
}