本文目錄
1. 什么是文件
1.1 程序文件
1.2 數(shù)據(jù)文件
1.3 文件名
2. 文件的打開和關(guān)閉
2.1 文件指針
2.2 文件的打開和關(guān)閉
3. 文件的順序讀寫
3.1 順序讀寫函數(shù)介紹
4. 文件的隨機(jī)讀寫
4.1 fseek
4.2 ftell
4.3 rewind
5. 文本文件和二進(jìn)制文件
6. 文件讀取結(jié)束的判定
6.1 被錯誤使用的 feof?
7. 文件緩沖區(qū)
1. 什么是文件
磁盤上的文件是文件。
但是在程序設(shè)計中,我們一般談的文件有兩種:程序文件、數(shù)據(jù)文件(從文件功能的角度來分類的)。
1.1 程序文件
包括源程序文件(后綴為.c),目標(biāo)文件(windows環(huán)境后綴為.obj),可執(zhí)行程序(windows環(huán)境后綴為.exe)。
1.2 數(shù)據(jù)文件
文件的內(nèi)容不一定是程序,而是程序運行時讀寫的數(shù)據(jù),比如程序運行需要從中讀取數(shù)據(jù)的文件,或者輸出內(nèi)容的文件。
本文討論的是數(shù)據(jù)文件。
在之前的學(xué)習(xí)中所處理數(shù)據(jù)的輸入輸出都是以終端為對象的,即從終端的鍵盤輸入數(shù)據(jù),運行結(jié)果顯示到顯示器上。
其實有時候我們會把信息輸出到磁盤上,當(dāng)需要的時候再從磁盤上把數(shù)據(jù)讀取到內(nèi)存中使用,這里處理的就是磁盤上文件。
1.3 文件名
一個文件要有一個唯一的文件標(biāo)識,以便用戶識別和引用。
文件名包含3部分:文件路徑+文件名主干+文件后綴
例如: c:\code\test.txt?
為了方便起見,文件標(biāo)識常被稱為文件名。
2. 文件的打開和關(guān)閉
2.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結(jié)構(gòu)的變量,并填充其中的信息,使用者不必關(guān)心細(xì)節(jié)。
一般都是通過一個FILE的指針來維護(hù)這個FILE結(jié)構(gòu)的變量,這樣使用起來更加方便。
下面我們可以創(chuàng)建一個FILE*的指針變量:
FILE* pf;//文件指針變量
定義pf是一個指向FILE類型數(shù)據(jù)的指針變量。可以使pf指向某個文件的文件信息區(qū)(是一個結(jié)構(gòu)體變量)。通過該文件信息區(qū)中的信息就能夠訪問該文件。也就是說,通過文件指針變量能夠找到與它關(guān)聯(lián)的文件。
比如:
2.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 );
打開方式如下:
文件使用方式 | 含義 | 如果指定文件不存在 |
---|---|---|
“r”(只讀) | 為了輸入數(shù)據(jù),打開一個已經(jīng)存在的文本文件 | 出錯 |
“w”(只寫) | 為了輸出數(shù)據(jù),打開一個文本文件 | 建立一個新的文件 |
“a”(追加) | 向文本文件尾添加數(shù)據(jù) | 建立一個新的文件 |
“rb”(只讀) | 為了輸入數(shù)據(jù),打開一個二進(jìn)制文件 | 出錯 |
“wb”(只寫) | 為了輸出數(shù)據(jù),打開一個二進(jìn)制文件 | 建立一個新的文件 |
“ab”(追加) | 向一個二進(jìn)制文件尾添加數(shù)據(jù) | 建立一個新的文件 |
“r+”(讀寫) | 為了讀和寫,打開一個文本文件 | 出錯 |
“w+”(讀寫) | 為了讀和寫,建立一個新的文件 | 建立一個新的文件 |
“a+”(讀寫) | 打開一個文件,在文件尾進(jìn)行讀寫 | 建立一個新的文件 |
“rb+”(讀寫) | 為了讀和寫打開一個二進(jìn)制文件 | 出錯 |
“wb+”(讀寫) | 為了讀和寫,新建一個新的二進(jìn)制文件 | 建立一個新的文件 |
“ab+”(讀寫) | 打開一個二進(jìn)制文件,在文件尾進(jìn)行讀和寫 | 建立一個新的文件 |
實例代碼:
/* 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;
}
3. 文件的順序讀寫
3.1 順序讀寫函數(shù)介紹
功能 | 函數(shù)名 | 適用于 |
---|---|---|
字符輸入函數(shù) | fgetc | 所有輸入流 |
字符輸出函數(shù) | fputc | 所有輸出流 |
文本行輸入函數(shù) | fgets | 所有輸入流 |
文本行輸出函數(shù) | fputs | 所有輸出流 |
格式化輸入函數(shù) | fscanf | 所有輸入流 |
格式化輸出函數(shù) | fprintf | 所有輸出流 |
二進(jìn)制輸入 | fread | 文件 |
二進(jìn)制輸出 | fwrite | 文件 |
4. 文件的隨機(jī)讀寫
4.1 fseek
根據(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;
}
4.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-portable
size = ftell(pFile);
fclose(pFile);
printf("Size of myfile.txt: %ld bytes.\n", size);
}
return 0;
}
4.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;
}
5. 文本文件和二進(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;
}
6. 文件讀取結(jié)束的判定
6.1 被錯誤使用的 feof?
牢記:在文件讀取過程中,不能用feof函數(shù)的返回值直接來判斷文件的是否結(jié)束。
?feof 的作用是:當(dāng)文件讀取結(jié)束的時候,判斷是讀取結(jié)束的原因是否是:遇到文件尾結(jié)束。
????????1. 文本文件讀取是否結(jié)束,判斷返回值是否為 EOF ( fgetc ),或者 NULL ( fgets )
? ? ? ? 例如:
? ? ? ? ? ? ? ? ○???fgetc 判斷是否為 EOF .
? ? ? ? ? ? ? ? ○???fgets 判斷返回值是否為 NULL .
????????2. 二進(jìn)制文件的讀取結(jié)束判斷,判斷返回值是否小于實際要讀的個數(shù)。
? ? ? ? 例如:
? ? ? ? ? ? ? ? ○? ?fread 判斷返回值是否小于實際要讀的個數(shù)。
正確的使用:
文本文件的例子:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int c; // 注意:int,非char,要求處理EOF
FILE* fp = fopen("test.txt", "r");
if (!fp) {
perror("File opening failed");
return EXIT_FAILURE;
}
//fgetc 當(dāng)讀取失敗的時候或者遇到文件結(jié)束的時候,都會返回EOF
while ((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 handling
if (feof(fp))
printf("Error reading test.bin: unexpected end of file\n");
else if (ferror(fp)) {
perror("Error reading test.bin");
}
}
fclose(fp);
}
7. 文件緩沖區(qū)
ANSIC 標(biāo)準(zhǔn)采用“緩沖文件系統(tǒng)”處理的數(shù)據(jù)文件的,所謂緩沖文件系統(tǒng)是指系統(tǒng)自動地在內(nèi)存中為程序中每一個正在使用的文件開辟一塊“文件緩沖區(qū)”。從內(nèi)存向磁盤輸出數(shù)據(jù)會先送到內(nèi)存中的緩沖區(qū),裝滿緩沖區(qū)后才一起送到磁盤上。如果從磁盤向計算機(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é)論:
因為有緩沖區(qū)的存在,C語言在操作文件的時候,需要做刷新緩沖區(qū)或者在文件操作結(jié)束的時候關(guān)閉文件。文章來源:http://www.zghlxwxcb.cn/news/detail-621487.html
如果不做,可能導(dǎo)致讀寫文件的問題。文章來源地址http://www.zghlxwxcb.cn/news/detail-621487.html
到了這里,關(guān)于【C語言】文件操作重點內(nèi)容梳理的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!