政府網(wǎng)站建設(shè)發(fā)展前景口碑營銷5t
目錄
1.為什么使用文件
2.什么是文件
2.1程序文件
2.2數(shù)據(jù)文件
2.3文件名
3.文件的打開和關(guān)閉
3.1文件指針
4.文件的順序讀寫
fputc()寫入文件
fgetc()從文件中讀取?
fgets()讀取一段字符串
fprintf格式化寫入文件、fscanf格式化讀出文件
4.1對比一組函數(shù)
5.文件的隨機(jī)讀寫
5.1fseek
5.2 ftell
5.3 rewind
6.文本文件和二進(jìn)制文件
7.文件讀取結(jié)束的判定
7.1被錯誤使用的feof
8.文件緩沖區(qū)
本章重點(diǎn)
- 為什么使用文件
- 什么是文件
- 文件的打開和關(guān)閉
- 文件的順序讀寫
- 文件的隨機(jī)讀寫
- 文本文件和二進(jìn)制文件
- 文件讀取結(jié)果的判定
- 文件緩沖區(qū)
1.為什么使用文件
????????在前面學(xué)習(xí)結(jié)構(gòu)體時,寫了通訊錄的程序,當(dāng)通訊錄運(yùn)行圈起來的時候,可以給通訊錄增加、刪除數(shù)據(jù)、此時數(shù)據(jù)是存放在內(nèi)存中,當(dāng)程序退出的時候,通訊錄中的數(shù)據(jù)自然就不存在了,等下次運(yùn)行通訊錄程序的時候,數(shù)據(jù)又得重新錄入,如果使用這樣的通訊錄就很難受。
? ? ? ? 我們在想既然是通訊錄就得把信息記錄下來,只有我們自己選擇刪除數(shù)據(jù)的時候,數(shù)據(jù)才不復(fù)存在。這就涉及到了數(shù)據(jù)持久化的問題,一般數(shù)據(jù)持久化的方法有,把數(shù)據(jù)存放在硬盤文件、存放到數(shù)據(jù)庫的方式。
? ? ? ? 使用文件我們可以將數(shù)據(jù)直接存放在電腦的硬盤上,做到了數(shù)據(jù)的持久化。
2.什么是文件
磁盤上的文件是文件。
但是在程序設(shè)計(jì)中,我們一般談的文件有兩種:程序文件、數(shù)據(jù)文件(從文件功能的角度來分類的)。
2.1程序文件
包括源程序文件(后綴為.c),目標(biāo)文件(windows環(huán)境下后綴為.obj),可執(zhí)行文件(windows環(huán)境后綴為.exe)。
2.2數(shù)據(jù)文件
文件的內(nèi)容不一定是程序,而是程序在運(yùn)行時讀寫的數(shù)據(jù),比如程序運(yùn)行需要從中讀取數(shù)據(jù)的文件,或者輸出內(nèi)容的文件。
本章討論的是數(shù)據(jù)文件。
在以前各章所處理數(shù)據(jù)的輸入輸出都是以終端為對象的,即從終端的鍵盤輸入數(shù)據(jù),運(yùn)行結(jié)果顯示到顯示器上。
其實(shí)有的時候我們需要把信息輸出到磁盤上,當(dāng)需要的時候再從磁盤上把數(shù)據(jù)讀取到內(nèi)存中使用,這里處理的就是磁盤上文件。
2.3文件名
一個文件要有一個唯一的文件標(biāo)識,以便用戶識別和引用。
文件名包含3部分:文件路徑 + 文件名主干 + 文件后綴
例如 c:\code\test.txt
為了方便起見,文件標(biāo)識常被稱為文件名。
3.文件的打開和關(guān)閉
3.1文件指針
緩沖文件系統(tǒng)中,關(guān)鍵的概念是 “文件類型指針”,簡稱 “文件指針”。
每個被使用的文件都在內(nèi)存中開辟了一個相應(yīng)的文件信息區(qū),用來存放文件的相關(guān)信息(如文件的名字,文件狀態(tài)及文件當(dāng)前的位置等)。這些信息是保存在一個結(jié)構(gòu)體變量中的,該結(jié)構(gòu)體類型是由系統(tǒng)聲明的,取名FILE.
例如,VS2013編譯環(huán)境提供的 stdio.h 頭文件中有以下的文件類型聲明:
struct _iobuf {char *_ptr;int ? _cnt;char *_base;int ? _flag;int ? _file;int ? _charbuf;int ? _bufsiz;char *_tmpfname;};
typedef struct _iobuf FILE;
不同的C編譯器的FILE類型包含的內(nèi)容不完全相同,但是大同小異。
每當(dāng)打開一個文件的時候,系統(tǒng)會根據(jù)文件的情況自動創(chuàng)建一個FILE的變量,并填充其中的信息,使用者不必關(guān)心其細(xì)節(jié)。
一般都是通過一個FILE的指針來維護(hù)這個FILE結(jié)構(gòu)的變量,這樣使用起來gengen更加方便。
下面我們可以創(chuàng)建一個FILE*的指針變量:
FILE* pf;//文件指針變量
?定義pf是一個指向FILE類型數(shù)據(jù)的指針變量,可以使pf指向某個文件的文件信息區(qū)(是一個結(jié)構(gòu)體變量)。通過該文件信息區(qū)中的信息就能夠訪問該文件。也就是說,通過文件指針變量能夠找到與它相關(guān)聯(lián)的文件。?
比如:
3.2文件的打開和關(guān)閉
文件在讀寫之前應(yīng)該先打開文件?,在試用結(jié)束之后應(yīng)該關(guān)閉文件。
在編寫程序的時候,再打開文件的同時,都會返回一個 FILE* 的指針變量指向該文件,也相當(dāng)于建立了指針和文件的關(guān)系
ANSIC 規(guī)定使用fopen函數(shù)來打開文件,fclose來關(guān)閉文件
//打開文件
FILE* fopen(const char* filename,const char* mode);
//關(guān)閉文件
int fclose(FILE* stream);
打開方式如下:
示例代碼:
/* fopen fclose example */
#include <stdio.h>
int main ()
{FILE * pFile;//打開文件pFile = fopen ("myfile.txt","w");//文件操作if (pFile != NULL){fputs ("fopen example",pFile);//關(guān)閉文件fclose (pFile);}return 0;
}
4.文件的順序讀寫
關(guān)于讀和寫的認(rèn)識:
?流的概念:
?fputc()寫入文件
//fputc()寫入文件
#include <stdio.h>
int main()
{FILE* pf = fopen("test.txt", "w");//test.txt必須在本文件下if (NULL == pf){perror("fopen");return 1;}//寫文件/*fputc('a', pf);fputc('b', pf);fputc('c', pf);fputc('d', pf);*/char ch = 0;for (ch = 'a'; ch <= 'z'; ch++){fputc(ch, pf);}//關(guān)閉文件fclose(pf);pf = NULL;return 0;
}
fgetc()從文件中讀取?
//fgetc()從文件中讀取
int main()
{FILE* pf = fopen("test.txt", "r");if (NULL == pf){perror("fopen");return 1;}//讀文件int ch = fgetc(pf);printf("%c\n", ch);ch = fgetc(pf);printf("%c\n", ch);ch = fgetc(pf);printf("%c\n", ch);//關(guān)閉文件fclose(pf);pf = NULL;return 0;
}
fgets()讀取一段字符串
//fgets()讀取一段字符串
int main()
{FILE* pf = fopen("test.txt", "r");if (NULL == pf){perror("fopen");return 1;}//讀文件//測試一行數(shù)據(jù)char buf[20] = {0};fgets(buf, 20, pf);printf("%s", buf);fgets(buf, 20, pf);printf("%s", buf);//關(guān)閉文件fclose(pf);pf = NULL;return 0;
}
fprintf格式化寫入文件、fscanf格式化讀出文件
//fprintf格式化寫入文件
#include <stdio.h>
struct S
{char name[20];int age;float score;
};int main()
{struct S s = { "zhangsan", 20, 95.5 };FILE* pf = fopen("test.txt", "w");if (NULL == pf){perror("fopen");return 1;}//格式化的寫入文件fprintf(pf, "%s %d %f\n", s.name, s.age, s.score);//關(guān)閉文件fclose(pf);pf = NULL;return 0;
}
//fscanf()格式化的讀取文件
int main()
{struct S s = { 0 };FILE* pf = fopen("test.txt", "r");if (NULL == pf){perror("fopen");return 1;}//格式化的讀取文件fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score));//打印看數(shù)據(jù)printf("%s %d %f\n", s.name, s.age, s.score);//關(guān)閉文件fclose(pf);pf = NULL;return 0;
}
4.1對比一組函數(shù)
scanf / fscanf / sscanf?
printf / fprintf / sprintf
5.文件的隨機(jī)讀寫
5.1fseek
根據(jù)文件指針的位置和偏移量來定位文件指針。
int fseek(FILE* stream, long int offset, int origin);//文件流 //偏移量 //起始位置
例子:
/* fseek example */
#include <stdio.h>
int main ()
{FILE * pFile;pFile = fopen ( "example.txt" , "wb" );fputs ( "This is an apple." , pFile );fseek ( pFile , 9 , SEEK_SET );fputs ( " sam" , pFile );fclose ( pFile );return 0;
}
5.2 ftell
返回文件指針相對于起始位置的偏移量
long int ftell ( FILE * stream );
例子:
/* ftell example : getting size of a file */
#include <stdio.h>
int main ()
{FILE * pFile;long size;pFile = fopen ("myfile.txt","rb");if (pFile==NULL) perror ("Error opening file");else{fseek (pFile, 0, SEEK_END); // non-portablesize=ftell (pFile);fclose (pFile);printf ("Size of myfile.txt: %ld bytes.\n",size);}return 0;
}
5.3 rewind
讓文件指針的位置回到文件的起始位置
void rewind ( FILE * stream );
例子:
/* rewind example */
#include <stdio.h>
int main ()
{int n;FILE * pFile;char buffer [27];pFile = fopen ("myfile.txt","w+");for ( n='A' ; n<='Z' ; n++)fputc ( n, pFile);rewind (pFile);fread (buffer,1,26,pFile);fclose (pFile);buffer[26]='\0';puts (buffer);return 0;
}
?
6.文本文件和二進(jìn)制文件
根據(jù)數(shù)據(jù)的組織形式,數(shù)據(jù)文件被稱為文本文件或者二進(jìn)制文件
數(shù)據(jù)在內(nèi)存中以二進(jìn)制的形式存儲,如果不加轉(zhuǎn)換的輸出到外存,就是二進(jìn)制文件。
如果要求在外存上以ASCII碼的形式存儲,則需要在存儲前轉(zhuǎn)換。以ASCII字符的形式存儲的文件就是文本文件。
一個數(shù)據(jù)在內(nèi)存中是怎么存儲的呢?
字符一律以ASCII形式存儲,數(shù)值型數(shù)據(jù)既可以用ASCII形式存儲,也可以使用二進(jìn)制形式存儲。
如有整數(shù)10000,如果以ASCII碼的形式輸出到磁盤,則磁盤中占用5個字節(jié)(每個字符一個字節(jié)),而
二進(jìn)制形式輸出,則在磁盤上只占4個字節(jié)(VS2013測試)。
測試代碼:
#include <stdio.h>
int main()
{int a = 10000;FILE* pf = fopen("test.txt", "wb");fwrite(&a, 4, 1, pf);//二進(jìn)制的形式寫到文件中fclose(pf);pf = NULL;return 0;
}
7.文件讀取結(jié)束的判定
7.1被錯誤使用的feof
牢記:在文件讀取過程中,不能用feof函數(shù)的返回值直接用來判斷文件的是否結(jié)束。而是應(yīng)用于當(dāng)文件讀取結(jié)束的時候,判斷是讀取失敗結(jié)束,還是遇到文件尾結(jié)束。
1. 文本文件讀取是否結(jié)束,判斷返回值是否為 EOF ( fgetc ),或者 NULL ( fgets )
????????例如:
????????????????fgetc 判斷是否為 EOF .
????????????????fgets 判斷返回值是否為 NULL .
2. 二進(jìn)制文件的讀取結(jié)束判斷,判斷返回值是否小于實(shí)際要讀的個數(shù)。
????????例如:
????????????????fread判斷返回值是否小于實(shí)際要讀的個數(shù)。
?正確的使用:
文本文件的例子:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{int c; // 注意:int,非char,要求處理EOFFILE* fp = fopen("test.txt", "r");if(!fp) {perror("File opening failed");return EXIT_FAILURE;}//fgetc 當(dāng)讀取失敗的時候或者遇到文件結(jié)束的時候,都會返回EOFwhile ((c = fgetc(fp)) != EOF) // 標(biāo)準(zhǔn)C I/O讀取文件循環(huán){putchar(c);}//判斷是什么原因結(jié)束的if (ferror(fp))puts("I/O error when reading");else if (feof(fp))puts("End of file reached successfully");fclose(fp);
}
二進(jìn)制文件的例子:
#include <stdio.h>
enum { SIZE = 5 };
int main(void)
{double a[SIZE] = {1.,2.,3.,4.,5.};FILE *fp = fopen("test.bin", "wb"); // 必須用二進(jìn)制模式fwrite(a, sizeof *a, SIZE, fp); // 寫 double 的數(shù)組fclose(fp);double b[SIZE];fp = fopen("test.bin","rb");size_t ret_code = fread(b, sizeof *b, SIZE, fp); // 讀 double 的數(shù)組if(ret_code == SIZE) {puts("Array read successfully, contents: ");for(int n = 0; n < SIZE; ++n) printf("%f ", b[n]);putchar('\n');} else { // error handlingif (feof(fp))printf("Error reading test.bin: unexpected end of file\n");else if (ferror(fp)) {perror("Error reading test.bin");}}fclose(fp);
}
8.文件緩沖區(qū)
ANSI C 標(biāo)準(zhǔn)采用“緩沖文件系統(tǒng)”處理的數(shù)據(jù)文件的,所謂緩沖文件系統(tǒng)是指系統(tǒng)自動地在內(nèi)存中為程序中每一個正在使用的文件開辟一塊“文件緩沖區(qū)”。從內(nèi)存向磁盤輸出數(shù)據(jù)會先送到內(nèi)存中的緩沖區(qū),裝滿緩沖區(qū)后才一起送到磁盤上。如果從磁盤向計(jì)算機(jī)讀入數(shù)據(jù),則從磁盤文件中讀取數(shù)據(jù)輸入到內(nèi)存緩沖區(qū)(充滿緩沖區(qū)),然后再從緩沖區(qū)逐個地將數(shù)據(jù)送到程序數(shù)據(jù)區(qū)(程序變量等)。緩沖區(qū)的大小根據(jù)C編譯系統(tǒng)決定的。?
?
#include <stdio.h>
#include <windows.h>
//VS2013 WIN10環(huán)境測試
int main()
{FILE*pf = fopen("test.txt", "w");fputs("abcdef", pf);//先將代碼放在輸出緩沖區(qū)printf("睡眠10秒-已經(jīng)寫數(shù)據(jù)了,打開test.txt文件,發(fā)現(xiàn)文件沒有內(nèi)容\n");Sleep(10000);printf("刷新緩沖區(qū)\n");fflush(pf);//刷新緩沖區(qū)時,才將輸出緩沖區(qū)的數(shù)據(jù)寫到文件(磁盤)//注:fflush 在高版本的VS上不能使用了printf("再睡眠10秒-此時,再次打開test.txt文件,文件有內(nèi)容了\n");Sleep(10000);fclose(pf);//注:fclose在關(guān)閉文件的時候,也會刷新緩沖區(qū)pf = NULL;return 0;
}
這里可以得出一個結(jié)論:
因?yàn)橛芯彌_區(qū)的存在,C語言在操作文件的時候,需要做刷新緩沖區(qū)或者在文件操作結(jié)束的時候關(guān)閉文件。如果不做,可能導(dǎo)致讀寫文件的問題
?