十年網(wǎng)站開發(fā)經(jīng)驗 + 多家企業(yè)客戶 + 靠譜的建站團隊
量身定制 + 運營維護+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
目錄
什么是文件
?文件分類
程序文件
數(shù)據(jù)文件
文件的使用
?文件指針
文件指針的使用
?文件的打開和關(guān)閉
文件的使用方式
?文件的順序讀寫
1.寫入一個字符
2.讀取一個字符
3.連續(xù)每次讀取一個字符
4.覆蓋并寫入一行數(shù)據(jù)
5.讀取指定長度的數(shù)據(jù)
6.將結(jié)構(gòu)體信息寫入文件中
7.讀取文件信息到結(jié)構(gòu)體變量中
8.二進制寫入文件
9.讀取二進制文件信息
10.sscanf()函數(shù)、sprintf()函數(shù)
文件的隨機讀寫
?fseek()函數(shù)
?ftell函數(shù)()
??rewind()函數(shù)
二進制文件和文本文件
文件讀取結(jié)束判定
?feof()函數(shù)
文本文件的判斷
二進制文件的判斷
📌————本章重點————📌
🔗文件指針
🔗文件的順序讀寫
🔗文件的隨機讀寫
🔗文件讀取結(jié)束判定
?————————————?
與普通文件載體不同,文件是以硬盤為載體存儲在計算機上的信息集合,文件可以是文本文檔、圖片、程序等等。文件通常具有點+三個字母的文件擴展名,用于指示文件類型(例如,圖片文件常常以KPEG格式保存并且文件擴展名為.jpg)。
? 將數(shù)據(jù)放入文件中,相比代碼程序中堆棧上的數(shù)據(jù),其優(yōu)點在于可以隨時做到需要時添加、舍棄時刪除,數(shù)據(jù)可以持久化。
文件分類:? 文件一般講兩種:程序文件和數(shù)據(jù)文件;
程序文件:包括源程序文件(后綴為.c),目標(biāo)文件(windows環(huán)境后綴為.obj),可執(zhí)行程序(windows環(huán)境后綴為.exe)。
數(shù)據(jù)文件:? 包括程序運行時所讀寫的數(shù)據(jù)。本篇所涉及的就是數(shù)據(jù)文件。
文件的操作一般分三步:1.打開文件;2.讀/寫;3.關(guān)閉文件;
? 想要對文件進行操作,“文件指針”就是一個關(guān)鍵橋梁(亦名:文件類型指針);????????????????
文件指針的使用:底層原理:每個被使用的文件,都在內(nèi)存中開辟了一個相應(yīng)的文件信息區(qū),用來存放文件的相關(guān)信息(如:文件名、文件狀態(tài)、文件位置等),這些信息被保存到一個結(jié)構(gòu)體中,系統(tǒng)為其聲明為FILE,每當(dāng)打開一個文件的時候,系統(tǒng)就會根據(jù)情況自動創(chuàng)建一個FILE結(jié)構(gòu)的變量,并且通過FILE*的指針來維護這個結(jié)構(gòu)。
FILE* pf;
定義一個文件指針變量pf,它可以指向某個文件的文件信息區(qū),通過其即可訪問到該文件。
在打開文件的同時,都會返回一個FILE*的指針變量指向該文件,也相當(dāng)于建立了指
針和文件的關(guān)系。
文件的使用方式:
- fopen() —— 打開文件;
- FILE * fopen ( const char * filename, const char * mode );
- fclose() —— 關(guān)閉文件;
- int fclose ( FILE * stream );
按常用序:
使用方式 | 作用? | 如果文件不存在 |
"r"(只讀) | 為了輸入數(shù)據(jù),打開一個已經(jīng)存在的文本文件 | 出錯? ? ? ? ? ? ? ?? |
"w"(只寫) | 為了輸出數(shù)據(jù),打開一個文本文件 | 建立一個新的文件 |
"a"(追加) | 向文本文件添加數(shù)據(jù) | 建立一個新的文件 |
"rb"(只讀) | 為了輸入數(shù)據(jù),打開一個二進制文件 | 出錯 |
"wb"(只寫) | 為了輸出數(shù)據(jù),打開一個二進制文件 | 建立一個新的文件 |
"ab"(追加) | 向一個二進制文件尾添加數(shù)據(jù) | 出錯 |
"r+"(讀寫) | 為了讀和寫,打開一個文本文件 | 出錯 |
"w+"(讀寫) | 為了讀和寫,建立一個新的文本文件 | 建立一個新的文件 |
"a+"(讀寫) | 打開一個文本文件,在文件尾進行讀寫 | 建立一個文件 |
"rb+"(讀寫) | 為了讀和寫,打開一個二進制文件 | 出錯 |
"wb+"(讀寫) | 為了讀和寫,建立一個新的二進制文件 | 建立一個新的文件 |
"ab+"(讀寫) | 打開一個二進制文件,在文件尾進行讀寫 | 建立一個新的文件 |
函數(shù)名 | 功能 | 適用性 |
fgetc() | 字符輸入函數(shù) | 所有輸入流 |
fputc() | 字符輸出函數(shù) | 所有輸出流 |
fgets() | 文本行輸入函數(shù) | 所有輸入流 |
fputs() | 文本行輸出函數(shù) | 所有輸出流 |
fscanf() | 格式化輸入函數(shù) | 所有輸入流 |
fprintf() | 格式化輸出函數(shù) | 所有輸出流 |
fread() | 二進制輸入 | 文件 |
fwrite() | 二進制輸出 | 文件 |
以上結(jié)合起來實例:
#include#include#includeint main()
{
FILE* pf= fopen("test.txt", "w+");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return;
}
//輸入一個字符
fputc('a', pf);
//用完關(guān)閉文件
fclose(pf);
pf = NULL;
return 0;
}
如圖示:在源文件所在目錄下,原本沒有test.txt文件,是w+創(chuàng)建了這個新的文件,并寫入一個字符a?
//寫文件
fputc('a', pf);
2.讀取一個字符://讀取一個字符
int ch = fgetc(pf);
if (ch != EOF)
{
printf("%c\n", ch);
}
3.連續(xù)每次讀取一個字符://文件中有abcdefg
int ch = fgetc(pf);
printf("%c\n", ch); //a
ch = fgetc(pf);
printf("%c\n", ch); //b
ch = fgetc(pf);
printf("%c\n", ch); //c
ch = fgetc(pf);
printf("%c\n", ch); //d
4.覆蓋并寫入一行數(shù)據(jù):fputs("hello world", pf);
注意:這里fputs函數(shù)雖然是整行寫入,但會覆蓋掉原始數(shù)據(jù)、
//定一一個數(shù)組
char arr[10] = { 0 };
fgets(arr, 5, pf); //將所讀取的數(shù)據(jù)放入arr中
printf("%s\n", arr);
這里的結(jié)構(gòu)體信息就是格式化的,那么就需要fprintf()函數(shù)了
#includetypedef struct S
{
char name[10];
int age;
}Peo;
int main()
{
FILE* pf = fopen("test.txt", "w");
if (pf != NULL)
{
Peo p = { "zhangsan", 18 };
fprintf(pf, "%s %d\n", p.name, p.age);
fclose(pf);
pf = NULL;
}
return 0;
}
#includetypedef struct S
{
char name[10];
int age;
}Peo;
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf != NULL)
{
Peo p = { 0 };
fscanf(pf, "%s %d", p.name, &p.age);
printf("%s %d", p.name, p.age);
fclose(pf);
pf = NULL;
}
return 0;
}
- size_t fwrite( const void *buffer, size_tsize, size_tcount, FILE *stream);
#include#include#includetypedef struct S
{
char name[10];
int age;
}Peo;
int main()
{
FILE* pf = fopen("test.txt", "wb+");
if (pf != NULL)
{
Peo p = { "lisi", 19};
fwrite(&p, sizeof(Peo), 1, pf);
fclose(pf);
pf = NULL;
}
return 0;
}
size_t fread( void *buffer, size_tsize, size_tcount, FILE *stream);
#includetypedef struct S
{
char name[10];
int age;
}Peo;
int main()
{
FILE* pf = fopen("test.txt", "rb+");
if (pf != NULL)
{
Peo p = { 0 };
fread(&p, sizeof(Peo), 1, pf);
printf("%s %d\n", p.name, p.age);
fclose(pf);
pf = NULL;
}
return 0;
}
這兩個函數(shù)雖然和文件操作關(guān)系不大,但是容易與文件操作函數(shù)混淆;
- int sscanf( const char *buffer, const char *format[,argument] ... );
- 將一個字符串轉(zhuǎn)化為格式化數(shù)據(jù);
#includetypedef struct S
{
char name[10];
int age;
}Peo;
int main()
{
//定義一個字符串
char buffer[] = { "zhansan 19" };
//定義一個結(jié)構(gòu)但不賦值
Peo p = { 0 };
sscanf(buffer, "%s %d", p.name, &p.age);
return 0;
}
int sprintf( char *buffer, const char *format[,argument] ... );
將一個格式化數(shù)據(jù)轉(zhuǎn)化為字符串;
#includetypedef struct S
{
char name[10];
int age;
}Peo;
int main()
{
//定義一個結(jié)構(gòu)
Peo p = { "zhangsan",19};
//定義一個字符串
char buffer[50] = { 0 };
sprintf(buffer, "%s %d\n", p.name, p.age);
return 0;
}
所謂的隨機讀寫,其實就是指定我們想要讀寫的位置。
fseek()函數(shù):
- 該函數(shù)可以從定位位置的偏移量處開始讀寫;
- int fseek( FILE *stream, long offset, int origin );
文件流? ? ? ? ??偏移量? ? 起始位置?- 返回值:
- 如果成功,fseek返回0;
- 否則,它返回一個非零值;
- 在無法查找的設(shè)備上,返回值未定義;? ? ? ??
三種定位指針:
使用實例:
#include#include#includeint main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return;
}
//開始多次讀取
//定位指針:比如要讀取從頭開始向后偏移 2 個單位的一個字符
fseek(pf, 2, SEEK_SET);
int ch = fgetc(pf);
printf("%c\n", ch);
//第二次讀?。阂玫疆?dāng)前文件指針?biāo)幬恢孟蚝笃?個單位的字符
fseek(pf, 5, SEEK_CUR);
ch = fgetc(pf);
printf("%c\n", ch);
//第三次讀?。阂玫轿募髂┪蚕蚯捌?個單位的一個字符
fseek(pf, -8, SEEK_END);
ch = fgetc(pf);
printf("%c\n", ch);
fclose(pf);
pf = NULL;
return 0;
}
ftell函數(shù)():特別說明:
在每使用完一次fseek函數(shù)后,文件指針會自動向后移動一位:
- 該函數(shù)可以返回文件指針相對于起始位置的偏移量;
- long int ftell ( FILE * stream );
使用實例:
我們直接在上一段代碼的基礎(chǔ)上加上ftell()函數(shù)即可直觀得到每次文件指針?biāo)幍奈恢茫?/p>
- 讓文件指針回到文件初始位置;
- void rewind ( FILE * stream );
使用實例:
我們知道數(shù)據(jù)在內(nèi)存中是以二進制形式存儲的,對于文件而言:如果不加轉(zhuǎn)換直接輸出到外存就是二進制文件;如果要在外存上以ASCII碼形式存儲,就需要提前轉(zhuǎn)換最后以ASCII碼值形式存儲的文件就是文本文件。
對于字符,一律使用ASCII碼形式存儲,但對于數(shù)值型數(shù)據(jù),即可以使用ASCII碼存儲也可以使用二進制形式存儲。
舉例:
數(shù)字10000的兩種存儲形式:
二進制文件:
文本文件:
首先將10000分成'1','0','0','0','0',?這五個字符,用每個字符對應(yīng)的ASCII碼值進行轉(zhuǎn)換:
由此可見:對于10000在數(shù),如果以二進制形式存儲占用4個字節(jié),如果以ASCII碼存儲占用5個字節(jié)。試想:那對于數(shù)字1呢?
顯而易見,二進制文件存儲和文本文件存儲對不同范圍的數(shù)字可以做到節(jié)省空間。
對二進制文件深入理解:
#includeint main()
{
FILE* pf = fopen("test.txt", "wb");
int a = 10000;
if (pf != NULL)
{
fwrite(&a, 4, 1, pf);
fclose(pf);
pf = NULL;
}
return 0;
}
對于上面這段代碼,我們知道是將數(shù)值10000放入了test.txt文件中,但我們無法直接看到它在文件中的真實值,于是使用vs的二進制編輯器即可查看:
該函數(shù)被許多人錯誤用來判斷文件是否讀取結(jié)束,其實它的作用是判斷文件讀取結(jié)束的原因;
文本文件的判斷:文件讀取結(jié)束有兩種情況:1.讀取過程中出現(xiàn)異常; 2.讀取到文件末尾;
要找出文件讀取是哪個原因,就分為以下情況:
文本文件:
- 如果用 fgetc() 讀取,要判斷 feof() 的返回值是否為EOF;
- 如果用 fgets() 讀取,要判斷 feof() 的返回值是否為NULL(0);
二進制文件:
都是使用 fread() 讀取,要判斷其返回值與指定讀取個數(shù)的大小,如果小于實際要讀的個數(shù),就說明發(fā)生讀取異常,如果等于實際要讀的個數(shù),就說明是因讀取成功而結(jié)束;
對于讀取異常的判斷,我們考慮判斷 ferror() 函數(shù)的返回值:
- 若ferrror()為真——異常讀取而結(jié)束;
- 若feof()為真——正常讀取到尾而結(jié)束;
#include#include#includeint main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen is failed !");
return;
}
int c = 0;
//由于要檢查EOF——EOF本質(zhì)是0——所以是int
while (c = fgetc(pf) != EOF)
{
putchar(c);
}
//直到while不執(zhí)行了—讀取結(jié)束了—判斷是什么原因結(jié)束的
if (ferror(pf))
{
printf("讀取中出現(xiàn)錯誤\n");
}
else if (feof(pf))
{
printf("讀取到文件尾\n");
}
fclose(pf);
pf = NULL;
return 0;
}
二進制文件的判斷:#include#include#includeint main()
{
FILE* pf = fopen("test.txt", "rb");
int arr[5] = { 0 };
if (pf == NULL)
{
return;
}
size_t num = fread(arr, sizeof(int), 5, pf);
if (num == 5)
{
//說明全部讀取成功
printf("Array read successfully\n");
}
else
{
//說明讀取不夠指定長度—判斷是什么原因
if (ferror(pf))
{
printf("讀取中出現(xiàn)錯誤\n");
}
else if (feof(pf))
{
printf("讀取到文件尾\n");
}
}
fclose(pf);
pf = NULL;
return 0;
}
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧