C語言文件操作講解
- 文件
- 文件名
- 文件類型
- 數據在內存中的存儲
- 文件緩沖區
- 文件指針
- 文件的打開與關閉
- fopen
- fclose
- fopen與fclose的使用
- 文件的打開方式
- 文件的順序讀寫
- fputc
- fgetc
- fputs
- fgets
- fprintf
- fscanf
- fwrite
- fread
- 輸入流與輸出流
- 對比scanf\fscanf\sscanf與printf\fprintf\sprintf
- sscanf與sprintf
- 文件的隨機讀寫
- fseek
- ftell
- rewind
- 文件結束判斷
文件
我們熟知,我們常見的文件就是磁盤上存儲的文件。在程序設計中,所涉及的文件有倆種:程序文件、數據文件
- 程序文件:
包括源程序文件(后綴為.c),目標文件(windows環境后綴為.obj),可執行程序(Windows環境后綴為.exe)
- 數據文件:
文件的內容不一定時程序,而是程序運行時讀寫的數據,比如程序運行需要從中讀取數據的文件,或者輸出內存的文件
在vs中使用printf和scanf處理數據的輸入輸出都是以終端為對象的,即從終端的鍵盤輸入數據,運行結果顯示到顯示器上。同時可以將信息輸出到磁盤上,當需要的時候將磁盤上的數據讀取到內存上使用,即處理磁盤上文件
文件名
一個文件有一個唯一的文件標識,以便用戶識別與引用。
- 文件名包含三部分:文件路徑+文件名主干+文件后綴
例如:D:\code\c_languege_2\c_2004_5_13(7)\c_2004_5_13(7)\test.c
文件標識常被稱為文件名
文件類型
根據數據的組織形式,數據文件被稱為文本文件或者二進制文件。
- 文本文件
數據原本在內存中以二進制形式存儲,如果要求在外存上以ASCII碼的形式存儲,則需要在存儲前轉換。以ASCII字符的形式存儲的文件就是文本文件。
- 二進制文件
數據在內存中以二進制的形式存儲,如果不加轉換的輸出到外存,就是二進制文件。
數據在內存中的存儲
字符一律以ASCII形式存儲,數值型數據既可以用ASCII形式存儲,也可以使用二進制形式存儲
以100000為例:
以ASCII碼形式輸出到磁盤:
1 0 0 0 0
相當于在磁盤中占用了五個字節,每個字符與每個字符都是分開計算的
00110001 00110000 00110000 00110000 00110000
以二進制形式輸出到磁盤(以電腦的存儲方式為小端為例)
10 27 00 00
以十六進制的形式顯示,每倆個數代表一個字節
00000000 00000000 00100111 00010000
使用代碼測試:
#include<stdio.h>
int main(void)
{int a = 0;//FILE后面介紹FILE* pf = fopen("test.txt", "wb");//以二進制形式寫到文中fwrite(&a, 4, 1, pf);fclose(pf);pf = NULL;return 0;
}
文件緩沖區
ASCII標準采用“緩沖文件系統”處理數據文件,所謂緩沖文件系統是指系統自動地在內存中為程序中每一個正在使用的文件開辟一塊“文件緩沖區”。從內存向磁盤輸出數據會先送到內存中的緩沖區,充滿緩沖區后才一起送到磁盤上。如果從磁盤向計算機讀入數據,則從磁盤文件中讀取數據輸入到內存緩沖區,充滿緩沖區后再從緩沖區逐個將數據送到程序數據區(程序變量等)。緩沖區的大小根據編譯系統決定。
文件指針
緩沖文件系統中,關鍵的概念是“文件類型指針”,簡稱“文件指針"。
每個被使用的文件都在內存中開辟了一個相應的文件信息區,用來存放文件的相關信息(如文件的名字,文件狀態及文件當前的位置等等)。這些信息是保存再一個結構體變量中的,這個結構體變量是由系統聲明的,為FILE.
- FILE在某些編譯器上的聲明
struct _iobuf
{char* _ptr;int _cnt;char* _base;int _flag;int _file;int _charbuf;int _bufsiz;char* _tmpfname;
};
typedef struct _iobuf FILE;
不同的編譯器定義的FILE類型包含的內容不完全相同。
每次打開文件時,系統會根據文件的情況自動創建一個FILE結構的變量,并填充信息。
- 使用指針維護FLIE結構的變量:
FILE* pf;
pf指針是一個指向FILE類型數據的指針變量,可以使pf指向某個文件的文件信息區,通過該文件信息區中的信息就可以訪問該文件,即通過指針變量找到與FILE相關聯的文件
文件的打開與關閉
fopen
- fopen的介紹:
FILE* fopen (const char* filename,const char* mode);
文件在讀寫之前打開文件
fclose
- fclose介紹
int fclose(FILE* stream);
在文件使用結束后關閉文件
fopen與fclose的使用
#include<stdio.h>
int main(void)
{FILE* pf = fopen("test.txt", "w");if(pf!=NULL){fputs("fopen",pf);fclose(pf);}return 0;
}
文件的打開方式
文件使用方式 | 含義 | 如果指定文件不存在 |
---|---|---|
“r”(只讀) | 為了輸入數據,打開一個已經存在的文本文件 | 出錯 |
“w”(只寫) | 為了輸出數據,打開一個文本文件 | 建立一個新文件 |
“a”(追加) | 向文本文件尾添加數據 | 出錯 |
“rb”(只讀) | 為了輸入數據,打開一個二進制文件 | 出錯 |
“wb”(只寫) | 為了輸出數據,打開一個二進制文件 | 建立一個新文件 |
“ab”(追加) | 向一個二進制文件尾添加數據 | 出錯 |
“r+”(讀寫) | 為了讀和寫,打開一個文本文件 | 出錯 |
“w+”(讀寫) | 為了讀和寫,建立一個新的文件 | 建立一個新文件 |
“a+”(讀寫) | 打開一個文件,在文件尾進行讀寫 | 建立一個新文件 |
“rb+”(讀寫) | 為了讀和寫,打開一個二進制文件 | 出差 |
“wb+”(讀寫) | 為了讀和寫,新建一個二進制文件 | 建立一個新文件 |
“ab+”(讀寫) | 打開一個二進制文件,在文件尾進行讀和寫 | 建立一個新文件 |
文件的順序讀寫
功能 | 函數名 | 適用于 |
---|---|---|
字符輸入函數 | fgets | 所以輸入流 |
字符輸出函數 | fputs | 所以輸入流 |
文本行輸入函數 | fgets | 所有輸入流 |
文本行輸出函數 | fputs | 所有輸出流 |
格式化輸入函數 | fscanf | 所有輸入流 |
格式化輸出函數 | fprintf | 所有輸出流 |
二進制輸入 | fread | 文件 |
二進制輸出 | fwrite | 文件 |
fputc
- fputc介紹:
int fputc ( int character, FILE * stream );
向流中輸入字符
int main(void)
{//以只寫的方式打開文件FILE* pf = fopen("test.txt", "w");if (pf == NULL){perror("fopen");return 1;}//向文件中寫字符fputc('h', pf);fputc('e', pf);fputc('l', pf);fputc('l', pf);fputc('o', pf);//關閉文件fclose(pf);pf = NULL;return 0;
}
fgetc
- fgetc介紹:
int fgetc ( FILE * stream );
從流中讀取字符
- fgetc實際使用:
int main(void)
{//以只讀的方式打開文件FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return 1;}//讀取文件中的字符char ch;ch = fgetc(pf);printf("%c ",ch);ch = fgetc(pf);printf("%c ", ch);ch = fgetc(pf);printf("%c ", ch);ch = fgetc(pf);printf("%c ", ch);//關閉文件fclose(pf);pf = NULL;return 0;
}
fputs
- fputs介紹:
int fputs ( const char * str, FILE * stream );
向流中輸入字符串
int main(void)
{//以只寫的方式打開文件FILE* pf = fopen("test.txt", "w");if (pf == NULL){perror("fopen");return 1;}//向文件中的寫字符串fputs("hello\n", pf);fputs("world", pf);//關閉文件fclose(pf);pf = NULL;return 0;
}
fgets
- fgets介紹:
char * fgets ( char * str, int num, FILE * stream );
從流中讀取字符串
int main(void)
{//以只讀的方式打開文件FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return 1;}//讀取文件中的字符串char ch[20];fgets(ch, 20, pf);printf("%s", ch);fgets(ch, 20, pf);printf("%s", ch);//關閉文件fclose(pf);pf = NULL;return 0;
}
fprintf
- fprintf介紹:
int fprintf ( FILE * stream, const char * format, … );
向流中輸入格式數據
int main(void)
{struct stu{char name[10];int age;char sex[5];};struct stu s = { "小明",6,"男"};//以只寫的方式打開文件FILE* pf = fopen("test.txt", "w");if (pf == NULL){perror("fopen");return 1;}//向文件中的輸入格式數據fprintf(pf, "%s %d %s", s.name, s.age, s.sex);//關閉文件fclose(pf);pf = NULL;return 0;
}
fscanf
- fscanf介紹:
int fscanf ( FILE * stream, const char * format, … );
向流中讀取格式化數據
int main(void)
{struct stu{char name[10];int age;char sex[5];};struct stu s = {0 };//以只讀的方式打開文件FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return 1;}//向文件中的輸入格式數據fscanf(pf, "%s %d %s", s.name, &(s.age), &(s.sex));printf("%s %d %s", s.name, s.age, s.sex);//關閉文件fclose(pf);pf = NULL;return 0;
}
fwrite
- fwrite介紹
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );向流中輸入二進制數據
struct stu
{char name[10];int age;char sex[5];
};int main(void)
{struct stu s = { "小明",20,"男" };FILE* pf = fopen("test.txt", "wb");if (pf == NULL){perror("fopen");return 1;}//向流中輸入二進制數據fwrite(&s, sizeof(struct stu), 1, pf);fclose(pf);pf==NULL;return 0;
}
fread
- fread介紹:
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );從流中讀取二進制的數據
struct stu
{char name[10];int age;char sex[5];
};int main(void)
{struct stu s = { 0 };FILE* pf = fopen("test.txt", "rb");if (pf == NULL){perror("fopen");return 1;}//從流中讀取二進制的數據fread(&s, sizeof(struct stu), 1, pf);printf("%s %d %s",s.name,s.age,s.sex);fclose(pf);pf == NULL;return 0;
}
輸入流與輸出流
與平常在學習c語言過程中使用scanf與printf類似,scanf將鍵盤上的數據讀取到內存上,printf將內存上的數據打印在屏幕上,內存面向文件時也是同樣的操作,使用fputs,fputc,fprintf將數據輸出到文件中,使用fgets,fgetc,fscanf將數據從文件讀取到內存中。
在面向文件時,數據會通過一種方式來與文件進行輸入輸出,而數組不僅僅可以面向文件,還可以面向屏幕,文件,網絡,網盤等等,這時就需要一種流來將數據傳輸在不同的輸出設備上,以便程序員使用。
在任何一個C語言程序運行的時候都會默認打開三個流:stdin,stdout,stderr.
printf與scanf都是僅適用于標準輸入流,標準輸出流
struct stu
{char name[10];int age;char sex[5];
};int main(viod)
{struct stu s = { 0 };//鍵盤格式化輸入數據fscanf(stdin, "%s %d %s", s.name, &(s.age), &(s.sex));//屏幕格式化輸出數據fprintf(stdout, "%s %d %s", s.name, s.age, s.sex);return 0;
}
對比scanf\fscanf\sscanf與printf\fprintf\sprintf
-
scanf與printf
scanf——從鍵盤上讀取格式化的數據(stdin)
printf——把數據寫到(輸出)屏幕(stdout) -
fscanf與fprintf
fscanf——針對所有輸入流的格式化的輸入函數(stdin,打開的文件等等)
fprintf——針對所有輸出流的格式化的輸出函數(stdout,打開的文件等等)
- sscanf與sprintf
sscanf——從一個字符串中還原一個格式化數據
sprintf——把一個格式化的數據,存放在(轉換成)一個字符串
sscanf與sprintf
- sscanf介紹
- sprintf介紹
sscanf與sprintf使用:
struct stu
{char name[10];int age;char sex[5];
};int main(void)
{struct stu s = { "小明",20,"男" };//sprintf——把一個格式化的數據,存放在(轉換成)一個字符串char ch[100] = { 0 };sprintf(ch, "%s %d %s", s.name, s.age, s.sex);printf("%s\n", ch);//sscanf——從一個字符串中還原一個格式化數據struct stu tmp = { 0 };sscanf(ch, "%s %d %s", tmp.name, &(tmp.age), &(tmp.sex));printf("%s %d %s", tmp.name, tmp.age, tmp.sex);return 0;
}
文件的隨機讀寫
fseek
- fseek介紹:
int fseek ( FILE * stream, long int offset, int origin );
根據文件指針位置和偏移量來定位文件指針
int main(void)
{FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return 1;}else{char tmp;tmp = fgetc(pf);printf("%c\n",tmp);tmp = fgetc(pf);printf("%c\n", tmp);fseek(pf, -1, SEEK_CUR);tmp = fgetc(pf);printf("%c\n", tmp);fseek(pf, -1, SEEK_CUR);tmp = fgetc(pf);printf("%c\n", tmp);return 0;}
}
ftell
- ftell介紹
long int ftell ( FILE * stream );
返回文件指針相對于起始位置的偏移量
int main(void)
{FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return 1;}else{char tmp;tmp = fgetc(pf);printf("%c\n",tmp);tmp = fgetc(pf);printf("%c\n", tmp);fseek(pf, -1, SEEK_CUR);tmp = fgetc(pf);printf("%c\n", tmp);fseek(pf, -1, SEEK_CUR);tmp = fgetc(pf);printf("%c\n", tmp);//與起始位置的偏移量int num = ftell(pf);printf("%d\n",num);return 0;}
}
rewind
- rewind介紹
void rewind ( FILE * stream );
讓文件指針的位置回到文件的起始位置
int main(void)
{FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return 1;}else{char tmp;tmp = fgetc(pf);printf("%c\n",tmp);tmp = fgetc(pf);printf("%c\n", tmp);fseek(pf, -1, SEEK_CUR);tmp = fgetc(pf);printf("%c\n", tmp);fseek(pf, -1, SEEK_CUR);tmp = fgetc(pf);printf("%c\n", tmp);//與起始位置的偏移量int num = ftell(pf);printf("%d\n",num);//回到起始位置rewind(pf);tmp = fgetc(pf);printf("%c\n", tmp);return 0;}
}
文件結束判斷
- feof介紹
int feof ( FILE * stream );
文件讀取結束的判斷:
- 在文件讀取過程中,不能使用feof函數的返回值直接來判斷文件是否結束
feof——應用于當文件讀取結束的時候,判斷是讀取失敗還是遇到文件尾結束
1.文本文件讀取結束,判斷返回值是否為EOF(fgetc),或者NULL(fgets),fgetc判斷是否為EOF,fgets判斷返回值是否為NULL.
2.二進制文件讀取結束判斷,判斷返回值是否小于實際要讀的個數,fread判斷返回值是否小于實際要讀的個數
文件讀取結束之后,需要判斷讀取結束的原因:
feof——返回真,說明是文件正常讀取遇到了結束標志而結束的
ferror——返回真,說明是文件在讀取過程中出錯而結束的(外部原因)