C語言文件操作零基礎新手入門保姆級教程
一、前言
我們如何使我們設計的程序具有“記憶功能”呢?答案是將數據以文件的形式另外保存。保存的形式有很多,在本文中我們以最簡單的文本形式保存在記事本上,相信這篇文章一定讓你學會。
二、文件操作基礎知識
①什麼是文件
我們一般談的文件有兩種:程序文件、數據文件
<程序文件>
包括源程序文件(後綴為.c),目標文件(windows環境後綴為.obj),可執行程序(windows環境後綴為.exe)。
<數據文件>
文件的內容不一定是程序,而是程序運行時讀寫的數據,比如程序運行需要從中讀取數據的文件,或者輸出內容的文件。本章討論的是數據文件
②數據文件類型
文本文件與二進制文件
數據在內存中以二進制的形式存儲,如果不加轉換的輸出到外存,就是<二進制文件>(後綴.bin)。
如果要求在外存上以ASCII碼的形式存儲,則需要在存儲前轉換。以ASCII字符的形式存儲的文件就是<文本文件>(後綴.txt)。
③數據如何存儲
一個數據在內存中是怎麼存儲的呢?
字符一律以ASCII形式存儲,數值型數據既可以用ASCII形式存儲,也可以使用二進制形式存儲。
如有整數10000,如果以ASCII碼的形式輸出到磁盤,則磁盤中占用5個字節(按照一個字符一個字符儲存,1 0 0 0 0每個字符一個字節),而二進制形式輸出,則在磁盤上隻占4個字節(int)
④如何讀取二進制文件
文本文件是我們通過記事本可以直接理解讀取的,而二進制文件如果以記事本的形式打開是我們不能識別的亂碼。但vs可以以某種方式讀取二進制文件,方法如下:
(下圖表示以記事本讀取以二進制形式保存的10000)
演示二進制文件打開方式:
第一步:將文件添加到vs下
第二步:右擊後打開方式選擇二進制編譯器
這就是最後的效果瞭。
⑤什麼是文件名
文件名包含3部分:文件路徑+文件名主幹+文件後綴
例如: c: \code\ test.txt (在後文會提到對文件名的絕對引用和相對引用)
⑥文件緩沖區
含義:ANSIC 標準采用“緩沖文件系統”處理的數據文件。所謂緩沖文件系統是指系統自動地在內存中為程序中每一個正在使用的文件開辟一塊“文件緩沖區”。文件緩沖區是用以暫時存放讀寫期間的文件數據而在內存區預留的一定空間。通過磁盤緩存來實現,磁盤緩存本身並不是一種實際存在的存儲介質,它依托於固定磁盤,提供對主存儲器存儲空間的擴充,即利用主存中的存儲空間, 來暫存從磁盤中讀出(或寫入)的信息。。
特點:從內存向磁盤輸出數據會先送到內存中的緩沖區,裝滿緩沖區後才一起送到磁盤上。如果從磁盤向計算機讀入數據,則從磁盤文件中讀取數據輸入到內存緩沖區(充滿緩沖區),然後再從緩沖區逐個地將數據送到程序數據區(程序變量等)。緩沖區的大小根據C編譯系統決定的 。
[擴展-三種類型的緩存區] 鏈接-文件緩沖區
⑦文件指針
<文件指針>
緩沖文件系統中,關鍵的概念是“文件類型指針”,簡稱“文件指針”。每個被使用的文件都在內存中開辟瞭一個相應的文件信息區,用來存放文件的相關信息(如文件的名字,文件狀態及
文件當前的位置等)。這些信息是保存在一個結構體變量中的。該結構體類型是有系統聲明的,取名FILE。每當打開一個文件的時候,系統會根據文件的情況自動創建一個FILE結構的變量,並填充其中的信息,使用者不必關心細節。一般都是通過一個FILE的指針來維護這個FILE結構的變量,這樣使用起來更加方便。
FILE*p//文件類型指針
可以使pf指向某個文件的文件信息區(是一個結構體變量)。通過該文件信息區中的信息就能夠訪問該文件。也就是說,通過文件指針變量能夠找到與它關聯的文件。
三、文件操作函數
①fopen 與 fclose
1.fopen
<功能>打開文件
<參數> filename-文件名 mode-打開方式
<返回值>一個文件指針
(打開方式mode表)
2.fclose
<功能> 關閉文件
<參數> stream-文件指針
3.使用示范
#include<stdio.h> #include<sting.h> #include<errno.h> int main() { FILE*pw = fopen("test.txt","w");//打開的文件為“test.txt” 打開方式為“w” if(pw==NULL)//若打開失敗則說明失敗原因並結束進程 { printf("%s",strerror(errno));//errno是全局錯誤變量 strerror將errno解析為錯誤原因 return 0; } fclose(pw);//關閉文件 pw = NULL;//將指針置為空,防止被誤用 return 0; }
文件名相對/絕對路徑
絕對路徑:如c:\code\test.txt 包含文件路徑 文件主幹 文件後綴
相對路徑:上圖的表示就是相對路徑,表示txt文件與源文件在同一路徑下,若要表示上一路 勁,則用“../”,一次類推上上路徑就是“../../”
FILE*pw = fopen("../test.txt","w");
②fputc與fgetc
1.fputc——輸出函數
<功能>將一個字符寫入流中
<參數>c-輸出的字符
<返回值>正常——返回輸出的字符
錯誤——返回EOF
<適用>所有流
2.fgetc——輸入函數
<功能>從流中讀取一個字符
<返回值>int——返回輸入的字符
EOF——發生錯誤或到達文件結尾
<適用>所有流
3.使用示范
#include <stdio.h> #include <string.h> #include<errno.h> //fputc 輸出一個字母 int main() { FILE*pw = fopen("test.txt","w"); if (pw == NULL) { printf("%s",strerror(errno)); return 0; } fputc('b',pw);//將字符‘b'寫入文件"test.txt" fclose(pw); pw = NULL; return 0; } //fgetc 讀取一個字符,讀取一個後文件指針往後偏移一位 int main() { FILE*pr = fopen("test.txt","r"); if (pr == NULL) { printf("%s",strerror(errno)); return 0; } char ch = 0;//用ch接收輸入的字符 ch = fgetc(pr);//從文件"test.txt"中讀取,讀取一個字符後文件指針自動往後移一位 fclose(pr); pr = NULL; printf("%c",ch); return 0; }
4.對所有流的理解
什麼是流:流是指信息從外部輸入設備(如鍵盤)向計算機內部(如內存)輸入和從內存 向外部輸出設備(顯示器)輸出的過程。
上述代碼演示的是對文件流的操作,我們在用標準輸入(stdin)輸出(stdout)流演示一下
③fputs與fgets
1.fputs——輸出函數
<功能>將字符串寫入流中
<返回值>int——非負值表示成功
EOF——發生錯誤
<適用>所有流
2.fgets——輸入函數
<功能>從流中獲取字符串
<返回值>正常——返回字符串
NULL——表示錯誤或者到達文件結尾
<參數>n-從流中讀取的最大字符數(\0會自動占去一位)
3.使用示范
#include <stdio.h> #include <string.h> #include<errno.h> //fputs int main() { FILE*pw = fopen("test.txt","w"); if (pw == NULL) { printf("%s",strerror(errno)); return 0; } char ch[5] = "abcd"; fputs(ch,pw); fclose(pw); pw = NULL; return 0; } //fgets int main() { FILE*pr = fopen("test.txt","r"); if (pr == NULL) { printf("%s",strerror(errno)); return 0; } char ch[5] = {0}; fgets(ch,3,pr);//有一位自動被\0占用 printf("%s",ch); return 0; }
④fprintf與fscanf
1.fprintf——輸出函數
<功能>將特定格式的數據寫入流中
<返回值>返回打印的字節數。
<適用>所有流
<對比>printf默認將數據打印在標準輸出流(stdout)上,而fprintf的輸出流可以選擇。printf 打印的字符數返回
2.fscanf——輸入函數
<功能>從流中讀取特定格式的數據
<適用>所有流
<返回值>返回成功轉換和分配的字段數量
3.使用示范
//頭文件同上略 //fprintf int main() { FILE*pw = fopen("test.txt","w"); if (pw == NULL) { printf("%s",strerror(errno)); return 0; } fprintf(pw,"%d %.2f %c",10,3.14,'a'); fclose(pw); pw = NULL; return 0; } //fscanf int main() { int a; float b; char c; FILE*pr = fopen("test.txt","r"); if (pr == NULL) { printf("%s",strerror(errno)); return 0; } fscanf(pr,"%d %f %c",&a,&b,&c); printf("%d %.2f %c", a,b,c); return 0; }
⑤fwrite與fread
1.fwrite——輸出函數
<功能>將數據以二進制的形式寫入文件流中
<參數>buffer-指向寫入數據的指針 size-每一個元素的大小 count-寫入的最大元素數
<返回值>實際寫入的元素數
<適用>文件流
2.fread——輸入函數
<功能>將數據以二進制的形式從文件流中讀取
<返回值>實際讀入的元素數
<適用>文件流
3.使用示范
//頭文件同上略 //fwrite fread typedef struct stu { int n; float score; char name[10]; }stu; int main() { stu s = {10,100.0,"張三"}; FILE*pr = fopen("test.txt","rb"); struct stu s1 = { 0 }; if (pr == NULL) { printf("%s",strerror(errno)); return 0; } //fwrite(&s,sizeof(stu),1,pw); fread(&s1,sizeof(stu),1,pr); fclose(pr); pr = NULL; return 0; }
⑥fseek與ftell與rewind
1.fseek
<功能>將文件指針移到指定的位置
<參數>offset-偏移量 origin-初始化文件指針位置
origin有三種選擇:SEEK_CUR 從當前位置開始
SEEK_END 從文件結尾開始
SEEK_SET 從文件開頭開始
<返回值>成功返回0,失敗返回非0值
<註意>對文件指針的修改不可以通過p++的方式實先
使用演示(現已知記事本儲存數據為“abcdef”)
//fseek int main() { FILE*pr = fopen("test.txt","r"); int a = 0; if (pr == NULL) { printf("%s",strerror(errno)); return 0; } //定位文件指針 fseek(pr, -1, SEEK_END);//指向最後一個的後一個 a=fgetc(pr); b=ftell(pr);//返回當前指針位置 printf("%c",a); printf("%d",b); fclose(pr); pr = NULL; return 0; }
(若初始化文件指針為SEEK_SET,偏移1則得到b)
2.ftell
<功能>返回當前文件指針的偏移量
3.rewind
<功能>使文件指針回到文件的開始
使用演示
int main() { int n; FILE * pFile; char buffer[27]; pFile = fopen("test.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; }
⑦ferror與feof
1.ferror
<功能>檢查流是否發生瞭錯誤
<返回值>若發生錯誤則返回0,否則返回非0值
2.feof
<功能>當文件讀取結束的時候,判斷是讀取失敗結束,還是遇到文件尾結束
<返回值>若不是文件末尾則返回0,是文件末尾則返回非0值
<註意>不能用feof函數的返回值直接用來判斷文件的是否結束
1. 文本文件讀取是否結束,判斷返回值是否為EOF (fgetc),或者NULL(fgets)
例如:
fgetc判斷是否為EOF.
fgets判斷返回值是否為NULL.
2. 二進制文件的讀取結束判斷,判斷返回值是否小於實際要讀的個數。
例如:
fread判斷返回值是否小於實際要讀的個數
使用演示(對於文本文件)
int main(void) { int c; // 註意:int,非char,要求處理EOF,EOF本質上是-1 FILE* fp = fopen("test.txt", "r"); if (!fp) { perror("File opening failed"); return EXIT_FAILURE; } //fgetc 當讀取失敗的時候或者遇到文件結束的時候,都會返回EOF while ((c = fgetc(fp)) != EOF) // 標準C I/O讀取文件循環 { putchar(c); } //判斷是什麼原因結束的 if (ferror(fp)) puts("I/O error when reading"); else if (feof(fp)) puts("End of file reached successfully"); fclose(fp); }
使用演示(對於二進制文件)
enum { SIZE = 5 }; int main(void) { double a[SIZE] = { 1.0, 2.0, 3.0, 4.0, 5.0 }; double b = 0.0; size_t ret_code = 0; FILE *fp = fopen("test.bin", "wb"); // 必須用二進制模式 fwrite(a, sizeof(*a), SIZE, fp); // 寫 double 的數組 sizeof(*)表示這種類型大小 fclose(fp); fp = fopen("test.bin", "rb"); // 讀 double 的數組 while ((ret_code = fread(&b, sizeof(double), 1, fp)) >= 1) { printf("%lf\n", b); } if (feof(fp)) printf("Error reading test.bin: unexpected end of file\n"); else if (ferror(fp)) { perror("Error reading test.bin"); } fclose(fp); fp = NULL; }
⑧補充函數 sscanf sprintf
1.sprintf
<功能>將特定格式的數據寫入字符串
<參數>buffer-輸出的儲存位置
<返回值>寫入字符串的數據大小(單位字節)
2.sscanf
<功能>從字符串中讀取特定格式的字符串
3.使用演示
//sprintf sscanf typedef struct stu { int n; float score; char name[10]; }stu; int main() { stu s = {10,3.14,"張三"}; char buf1[1024] = {0}; stu s1 = {0}; sprintf(buf1, "%d %f %s",s.n,s.score,s.name);//不要再加上n=...,score=... sscanf(buf1,"%d %f %s",&(s1.n),&(s1.score),&(s1.name)); printf("%d\n",s1.n); printf("%s\n",s1.name); return 0 ; }
⑨補充函數perror strerror
<比較>perror和strerror相比不需要引用errno變量,也不需要使用printf函數,操作更加便捷。同時自行輸入的字符串起到標識的作用,不會混淆。
總結
到此這篇關於C語言文件操作零基礎新手入門保姆級教程的文章就介紹到這瞭,更多相關C語言文件操作零基礎教程內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!