文件基本概念
-
文件分類
-
文本文件
- 以ASCII碼格式存放,一個字節(jié)存放一個字符.
-
二進(jìn)制文件
- 以二進(jìn)制存儲的
-
-
文本文件和二進(jìn)制文件的區(qū)別
- 1.存儲步驟不同
- 文本文件在存儲的時候,會先查詢需要存儲數(shù)據(jù)的ASCII碼, 然后再將對應(yīng)的ASCII 碼轉(zhuǎn)換為二進(jìn)制,然后再存儲;
- 2.從存儲步驟來看,文本文件需要縣查找再存儲, 所以效率會低一些
- 3.從內(nèi)存上的表現(xiàn), 文本文件會更占用內(nèi)存
- 1.存儲步驟不同
- 注意點:
- 任何的文本編輯器在打開文件的時候,都會以某種編碼方式去解碼存儲的數(shù)據(jù)
- 而文本編輯器默認(rèn)的解碼方式就是ASCII碼解碼
文件的打開和關(guān)閉
- fputc/ fputs/ fgetc/ fgets 這些函數(shù)都是用于操作文本文件的;
- fwrite / fread 這兩個函數(shù)就是用于操作二進(jìn)制文件的
- 也就是說:
- fputc/ fputs/ fgetc/ fgets保存的都是 ASCII碼
- fwrite / fread 保存的是二進(jìn)制
fopen()函數(shù)
- fopen函數(shù)返回的FILE * 是一個指向結(jié)構(gòu)體的指針;
- FILE *并不是打開的那個文件, 而是一個結(jié)構(gòu)體,這個結(jié)構(gòu)體中描述了被打開文件在緩沖區(qū)中的各種狀態(tài)
- 函數(shù)的聲明: FILE * fopen ( const char * filename, const char * mode );
- 兩個參數(shù)都是傳入字符串
- 第一個參數(shù): 需要打開的文件的名稱
- 第二個參數(shù): 打開文件的模式
- 返回值: 如果打開文件成功, 會返回文件的句柄, 如果打開失敗會返回NULL
- 模式的對應(yīng)取值
- r 讀取文件
- 文件不存在會報錯,文件存在就打開
- 只能讀不能寫
FILE *fp = fopen("abc.txt", "r"); if(fp == NULL){ printf("打開失敗\n"); }
- w 寫入文件
- 文件不存在會自動創(chuàng)建, 文件存在會直接打開
- 只能寫不能讀
FILE *fp = fopen("abc", "r"); if(fp == NULL){ printf("打開失敗"); }
- r+ 讀取和寫入文件
- 文件不存在會報錯,文件存在就會打開
- 既可以讀又可以寫
FILE *fp = fopen("abc.txt", "r+"); if(fp == NULL){ printf("打開失敗"); }
- w+ 讀取和寫入文件(重點)
- 文件不存在會自動創(chuàng)建, 文件存在會直接打開
- 既可以讀也可以寫
FILE *fp = fopen("abc.txt", "w+"); if(fp == NULL){ printf("打開失敗\n"); }
- a 追加寫入文件
- 文件不存在會自動創(chuàng)建, 文件存儲會直接打開
- 只能在原有數(shù)據(jù)的基礎(chǔ)上追加
FILE *fp = fopen("abc.txt", "a"); if(fp == NULL){ printf("打開失敗\n"); }
- a+ 追加寫入文件,并且可以讀取(重點)
- 文件不存在會自動創(chuàng)建, 文件存在會直接打開
- 可以在原有數(shù)據(jù)的基礎(chǔ)上追加, 并且還可以讀取數(shù)據(jù)
FILE *fp = fopen("abc.txt", "a+"); if(fp == NULL){ printf("打開失敗\n"); }
fclose()函數(shù)
- 格式: fclose(fp);
- 第一個參數(shù): 需要關(guān)閉的文件句柄
- 返回值: int 成功返回0, 失敗返回 EOF(-1);
- 一次寫入一個字符 (fputc函數(shù))
- fputc就是寫入一個字符
- fputc是以文本文件的形式保存數(shù)據(jù)
- 格式: int fputc (int ch, FILE * stream );
- 第一個參數(shù): 需要寫入到文件的字符
- 第二個參數(shù): 已經(jīng)打開文件句柄;
- 返回值:寫入成功,返回寫入成功的字符, 如果失敗, 返回 EOF
- EOF 是文件結(jié)束的標(biāo)識,本質(zhì)上就是-1;
FILE *fp = fopen("abc.txt", "w"); int res = fputc('a', fp); printf("res = %i\n", res); // 關(guān)閉打開的文件 fclose(fp);
一次讀取一個字符(fgetc()函數(shù))
- int fgetc ( FILE * stream );
- 第一個參數(shù): 被讀取的文件的 文件句柄
- 返回值:當(dāng)前獲取到的字符, 如果獲取失敗就會返回 EOF
FILE *fp = fopen("abc.txt", "w+"); fputc('a',fp); fputc('b',fp); fputc('c',fp); fputc('d',fp); // 每次操作完文件,文件的指針都會向后移動 // 將文件指針重新只想一個流的開頭 rewind(fp); // fgetc 被讀取的文件的 文件句柄 char ch ; while((ch = fgetc(fp)) != EOF){ printf("ch = %c\n", ch); }
文件末尾的判斷(feof()函數(shù))
-
feof()函數(shù)(了解一下)
- 返回值為0, 沒有讀到文件末尾
- 如果返回非0, 代表讀到了文件末尾
#include <stdio.h> int main() { FILE *fp = fopen("abc.txt", "w+"); fputc('a',fp); fputc('b',fp); fputc('c',fp); fputc('d',fp); // 將文件指針重新只想一個流的開頭 rewind(fp); // 按照下列的寫法, 會多讀取一個字符 // 原因就是feof函數(shù)在判斷文件指針時, 指針還沒有移動 // 只有寫入或者讀取過來指針才會移動 char ch ; // while(!feof(fp)){ // ch = fgetc(fp); // printf("ch = %c\n", ch); // } // 所以在使用feof函數(shù)的時候, 一定要先取, 然后再判斷feof // 否者會多取一個 while((ch = fgetc(fp)) && !feof(fp)){ printf("ch = %c\n", ch); } return 0; }
注意點:在使用feof函數(shù)的時候,一定要先取 ,然后再判斷Feof, 否則會奪取一個
定義函數(shù), 實現(xiàn)文件的加密和解碼
#include <stdio.h>
void encode(char *name, char *newName, int code);
int main()
{
// 編碼
//encode("abc.txt", "encode.txt", 6);
// 解碼
encode("encode.txt", "decode.txt", 6);
return 0;
}
void encode(char *name, char *newName, int code){
// 打開一個需要加密的文件
FILE *fr = fopen(name, "r+");
// 打開需要寫入加密內(nèi)容的文件
FILE *fw = fopen(newName, "w+");
// 不斷的讀 不斷的加密 不斷的寫入
char ch = EOF;
while((ch = fgetc(fr)) != EOF){
ch = ch ^ code;
fputc(ch, fw);
}
// 關(guān)閉已經(jīng)打開的文件
fclose(fr);
fclose(fw);
}
一次性寫入一行數(shù)據(jù)(fputs()函數(shù))
- fputs()函數(shù)可以一次寫入一堆字符
- 格式: fputs(const char * restrict _Str,FILE * restrict _File);
- 第一個參數(shù): 需要寫入的數(shù)據(jù)
- 第二個參數(shù): 寫入到那個文件的文件句柄
- 注意點: 不會再寫入字符的后面自動添加\n
// 打開一個文件
FILE *fp = fopen("abc.txt", "w+");
// 一次寫入一堆函數(shù):需要自己添加換行 \n
fputs("www.baidu.com\n", fp);
fputs("www.taobao.com\n", fp);
// 關(guān)閉打開的文件
fclose(fp);
一次性讀取一行數(shù)據(jù)(fgets()函數(shù))
-
fgets()函數(shù) 格式: fgets(char * restrict _Buf,int _MaxCount,FILE * restrict _File);
- 第一個參數(shù): 讀取出來的數(shù)據(jù)會放到這個參數(shù)的變量中
- 第二個參數(shù): 需要讀取多少個字節(jié)的數(shù)據(jù)
- 第三個參數(shù): 被讀取文件的句柄
- 注意點: 雖然告訴系統(tǒng)需要讀取1024個字符,但是只要遇到\n, fgets函數(shù)就會自動終止讀取
FILE *fp = fopen("abc.txt", "r+"); char buf[1024]; fgets(buf, 1024, fp); printf("buf = %s\n", buf); // 關(guān)閉打開的文件 fclose(fp);
-
注意點:
- 遇到\n自動結(jié)束,并且\n 也會被讀取進(jìn)來
- maxCount 指定多睡不一定會讀取多少, 讀取到\n會自動停止讀取
- 最多只能讀取 maxCount - 1 個字符, 會在最后自動添加 \0
- 遇到EOF也會自動停止讀取
#include <stdio.h> int main() { FILE *fp = fopen("abc.txt", "r+"); char buf[1024]; /* * abc.txt : hello world * www.baidu.com */ // 每次只能讀取n-1個字符, 會在末尾自動添加\0 fgets(buf, 4, fp); printf("buf = %s\n", buf); // hel fgets(buf, 1024, fp); printf("buf = %s\n", buf); // 關(guān)閉打開的文件 fclose(fp); return 0; }
遍歷寫法
// 推薦寫法
while(fgets(buf, 1024, fp) != NULL){
printf("buf = %s\n", buf);
}
// 不推薦寫法
while(fgets(buf, 1024, fp) && !feof(fp)){
printf("buf = %s\n", buf);
}
一次性寫入一塊數(shù)據(jù)(fwrite()函數(shù))
- 格式: fwrite(const void * restrict _Str,size_t _Size,size_t _Count,FILE * restrict _File);
- 第一個參數(shù): 需要寫入文件的數(shù)據(jù)地址
- 第二個參數(shù): 每次寫入多少個字節(jié)
- 第三個參數(shù): 需要寫入多少次
- 第四個參數(shù): 寫入到上面地方
- 返回值: 寫入了多少次就返回多少, 出錯或文件結(jié)束, 返回 0;
FILE *fp = fopen("abc.txt", "w+");
int ages[] = {1, 3, 5};
printf("sizeof(ages) = %i\n", sizeof(ages));
fwrite(&ages, sizeof(ages), 1, fp);
- 注意點:
- 一般情況下, 寫入一塊數(shù)據(jù),每次寫入多少個字節(jié)可以寫大一點, 而寫入多少次會寫小一點;
- 由于fwrite是以二進(jìn)制的形式寫入文件的, 所以和以文件的寫入不同, fwrite函數(shù)會忽略 \0 \n等內(nèi)容;
FILE *fp = fopen("abc.txt", "w+");
char *str = "hello\0world";
// fputs是以文本文件的形式存儲, 存儲的是ASCII
// 注意點: 如果利用fputs寫入字符串, 遇到\0會停止寫入
// fputs(str, fp);
// 注意點: 在二進(jìn)制中是沒有\(zhòng)n \0這些概念的, 寫入的時候不會受到\0 \n的影響
fwrite(str, 9, 1, fp);
一次性讀取一塊數(shù)據(jù)(fread()函數(shù))
- 格式: fread(void * restrict _DstBuf,size_t _ElementSize,size_t _Count,FILE * restrict _File);
- 第一個參數(shù): 讀取的數(shù)據(jù)存儲到哪
- 第二個參數(shù): 一次讀取多少個字節(jié)
- 第三個參數(shù): 讀取多少次
- 第四個參數(shù): 從什么地方讀取
- 返回值: 讀取了多少就返回多少, 如果返回0 代表讀取錯誤
FILE *fp = fopen("abc.txt", "w+");
// abc.txt : hello world
char buf[1024] = {0};
// fp對應(yīng)的文件中讀取1024次,每次讀取1個字節(jié), 將讀取的內(nèi)容放到buf中
fread(buf, 1, 1024, fp);
printf("buf = %s\n", buf);
- 注意點:
- fread(buf, 1, 1024, fp); 雖然告訴系統(tǒng)需要讀取1024次,但是只要讀取到EOF就不會讀取了
- 每次讀取多少個字節(jié), 一定要按照存儲的數(shù)據(jù)類型占用的內(nèi)存大小來讀取, 不要寫過大的值
FILE *fp = fopen("abc.txt", "wb+");
char *str1 = "123456";
fwrite(str1, sizeof(char), strlen(str1), fp);
rewind(fp);
char buf[1024] = {0};
while(fread(buf, 1, 1024, fp) > 0){
printf("buf = %s\n", buf);
}
讀寫結(jié)構(gòu)體
- 讀單個結(jié)構(gòu)體
#include <stdio.h>
typedef struct person{
char *name;
int age;
double height;
} Person;
int main()
{
// // 寫入一個結(jié)構(gòu)體
// Person p = {"www", 18, 1.78};
// FILE *fp = fopen("abc.txt", "wb+");
// fwrite(&p, sizeof(Person), 1, fp);
// // 關(guān)閉文件
// fclose(fp);
// 讀一個結(jié)構(gòu)體
FILE *fp = fopen("abc.txt", "rb+");
Person p;
fread(&p, sizeof(Person), 1, fp);
printf("name = %s\n", p.name);
printf("age = %i\n", p.age);
printf("height = %lf\n", p.height);
// 關(guān)閉文件
fclose(fp);
return 0;
}
- 讀寫結(jié)構(gòu)體數(shù)組
#include <stdio.h>
typedef struct person{
char name[8];
int age;
double height;
} Person;
int main()
{
// // 寫入一個結(jié)構(gòu)體數(shù)組
// Person ps[3] = {
// {"www", 38, 1.38},
// {"baidu", 18, 1.78},
// {"com", 98, 1.58},
// };
// FILE *fp = fopen("abc.txt", "wb+");
// fwrite(&ps, sizeof(ps), 1, fp);
// // 關(guān)閉文件
// fclose(fp);
// 讀一個結(jié)構(gòu)體數(shù)組
FILE *fp = fopen("abc.txt", "rb+");
Person ps[3];
fread(&ps, sizeof(Person), 3, fp);
for(int i = 0; i< 3; i++){
Person p = ps[i];
printf("name = %s\n", p.name);
printf("age = %i\n", p.age);
printf("height = %lf\n", p.height);
printf("----------\n");
}
// 關(guān)閉文件
fclose(fp);
return 0;
}