国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

怒肝1.5萬字——史上最全C語言文件操作詳解

Alfred / 3194人閱讀

摘要:二什么是文件磁盤上的文件就是文件。文件指針變量定義是一個指向類型數據的指針變量。表示向何種流中輸出,可以是標準輸出流,也可以是文件流。文件結構體指針,將要讀取的文件流。

提示:文章寫完后,目錄可以自動生成,如何生成可參考右邊的幫助文檔


一、為什么使用文件

我們前面學習結構體時,寫了通訊錄的程序,當通訊錄運行起來時,可以給通訊錄增加、刪除數據,此時的數據是存放在內存中的,當程序退出時,通訊錄的數據就被銷毀了,下次運行通訊錄時,數據又得重新錄入,如果使用這樣的通訊錄就會非常的坐牢。

所以這些通訊錄數據我們僅僅放在內存里是不行的,大家都知道,我們電腦的C盤、D盤、E盤等等里面存放的文件,你不進行刪除,它就一直在那里,那我們可以試著把通訊錄的數據存入磁盤里,來實現數據的持久性。

二、什么是文件?

磁盤上的文件就是文件。
但是在程序設計中,我們一般談的文件有兩種:
1.程序文件
2.數據文件
(以文件功能來劃分)

2.1程序文件

包括源程序文件(后綴為.c),目標文件(windows環境后綴為.obj),可執行程序(windows環境后綴為.exe)。

2.2數據文件

文件的內容不一定是程序,而是程序運行時讀寫的數據,比如程序運行需要從中讀取數據的文件,或者輸出內容的文件。
本文著重討論數據文件
在以前各章所處理的數據的輸入輸出都是以終端為對象的,即從終端的鍵盤輸入數據,運行的結果顯示到顯示器上,其實有時候我們會把信息輸出到鍵盤上,當需要的時候再從鍵盤上把數據讀取到內存中使用,這里處理的就是磁盤上的文件。

下圖是數據文件與程序文件的交互簡圖

2.3文件名

一個文件要有一個唯一的文件標識,便于用戶的識別與引用。
文件名包含3個部分:文件路徑+文件名主干+文件后綴
栗子:E:/c-language-notes/test.txt
這里的E:/c-language-notes/叫作文件路徑,是在E盤c-language-notes這個路徑底下
test叫作文件主干
.txt叫做文件后綴

為了方便起見,文件標識常被稱為文件名

三、文件的打開與關閉

3.1文件指針

緩沖文件系統中,關鍵的概念是“文件類型指針”,簡稱文件指針

每個被使用的文件都在內存中開辟了一個相應的文件信息區,用來存放文件的相關信息(如文件的名字,文件狀態及文件當前的位置等)。這些信息是保存在一個結構體變量中的。該結構體類型是由系統聲明的,結構體類型名為FILE(下文會提到)

如上圖,當你想要操作一個數據文件時,你不可避免地會經歷3個操作:打開文件、讀/寫文件、關閉文件。只要你打開文件,系統會自動生成一個叫做文件信息區的東西,也就是會創建一個 FILE 類型結構體變量,上圖以f作為示例,實際也可能是其他的,那么創建完成后,f就會和data.txt文件強關聯了,f會記錄data文件名、文件有多大、文件在哪個位置、文件的狀態是怎樣的。。。

vs2013的編譯器環境提供的stdio.h的頭文件中有以下的文件類型聲明:

struct _iobuf{	char*_ptr;	int _cnt;	char*_base;	int _flag;	int _file;	int _charbuf;	int _bufsiz;	char*_tmpfname;};typedef struct _ibuf FILE//把上述結構體重命名為FILE

不同編譯器的FILE類型包含的內容不一定完全相同,但基本都是大同小異,每當打開一個文件時,系統會根據文件的情況自動創建一個FILE結果的變量,并填充其中的信息,我們使用者不必過度關心細節,按周總理說的“求同存異”即可。

一般都是通過一個FILE類型的指針來維護FILE結構的變量,這樣使用起來更加方便。

FILE*pf;//文件指針變量

定義pf是一個指向FILE類型數據的指針變量。可以使pf指向某個文件的文件信息區(是一個結構體變量)。通過該文件信息區中的信息就可以訪問該文件。也就是說,通過文件指針變量能找到與它關聯的文件如下圖所示:
pf是指向文件信息區的,而文件信息區又可以確切的找到與它關聯的文件,這樣你就可以通過pf找到所需文件并進行相關操作。

3.2文件的打開與關閉

文件在讀寫之前應該先打開文件,在使用后應該關閉文件
在編寫程序時,打開文件的同時,都會返回一個FILE*的指針變量指向該文件,也相對于建立了指針和文件的聯系。

ANSIC規定使用fopen函數來打開文件,fclose來關閉文件

FILE *fopen(const char* filename, const char* moede);//打開文件//fopen第一個參數是文件名,第二個參數是打開模式//比如你傳一個test.txt到一個參數里,傳的其實是首字母t的地址//打開模式是這樣的,你是想給這個文件寫點東西還是想讀取這個文件的一些東西int fclose(FILE*stream);//關閉文件

關于打開模式如下圖,比如你打開模式是r,那你的打開方式就是讀,如果你的文件不存在或者沒有被找到,那fopen函數就會調用失敗

(圖片來自比特就業課,這里只舉r一個打開方式,其他打開方式讀者可自行對照上表)

fopen打開模式打開data.txt文件會返回一個FILE*的指針,該指針是指向與data.txt文件相關聯的文件信息區的起始地址,如果打開失敗會返回空指針。

fclose關閉文件相對fopen就簡單很多了,你要關閉哪個文件,我們直接傳那個文件關聯的文件信息區的指針即可,也就是上圖的pf

打開和關閉實際操作代碼示例如下:
比如我現在要打開E:/c-language-notes/test.21.10.9路徑下的data.txt文件

int main(){   //打開文件    //fopen函數	FILE*pf=fopen("E://c-language-notes//test.21.10.9//data.txt", "r");	//這里的"/"可能會與后面的字母構成轉義字符,我們用/對/進行轉義一下,讓/單純是一個/	//fopen函數會返回一個FILE*的指針,打開失敗返回空指針	if (pf == NULL)	{		perror("fopen");//perror函數顯示錯誤信息		return -1;	}	//讀文件。。。	//關閉文件	fclose(pf);	pf = NULL;	return 0;}

需要注意的是,fclose關閉文件是不會把pf置為空指針的,我們需要手動操作置為空指針

四、文件的順序讀寫


(圖片來自比特就業課)
所有輸入流包括:istream 類連續文本模式輸入使用、ifstream磁盤文件輸入、istringstream 類從內存字符串的輸入

4.1字符輸入輸出函數

fgetc和fputc函數分別是讀入一個字符和輸出一個字符

如上圖,我們寫一個程序時,會產生一些數據,數據會存放在內存中,如果你想把數據寫入(輸出)到文件里,或者你想把文件里的信息讀到內存中,叫讀(輸入操作)。我們用輸入/輸出操作就用的是fgetc與fputc

fputc函數示例:

// int fputc(int c, FILE *stream);函數聲明int main(){   	FILE*pf = fopen("E://c-language-notes//test.21.10.9//data.txt", "w");//以“只寫w”的模式打開文件	if (pf == NULL)	{		perror("fopen");//perror函數顯示錯誤信息		return -1;	}	//寫文件	fputc("b", pf);	fputc("i", pf);	fputc("t", pf);	fclose(pf);	pf = NULL;	return 0;}

運行完上述代碼后,相關文件自動出現fputc函數寫入的三個字

fgetc函數示例:

//int fgetc(FILE *filename);函數聲明int main(){	FILE*pf = fopen("E://c-language-notes//test.21.10.9//data.txt", "r");//以“只讀r”的模式打開文件	if (pf == NULL)	{		perror("fopen");//perror函數顯示錯誤信息		return -1;	}	//讀文件	int ch1=fgetc(pf);	printf("%c/n", ch1);	int ch2 = fgetc(pf);	printf("%c/n", ch2);		int ch3 = fgetc(pf);	printf("%c/n", ch3);	fclose(pf);	pf = NULL;	return 0;}

假設我們現在相關文件里有3個字母abc

我們運行上述程序,程序自動從文件里獲取三個字母abc

4.2文本行輸入輸出函數

我們再來看一看文本行輸入/輸出函數
fputs函數示例:

//int fputs(const char *s, FILE *stream);函數聲明//s 代表要輸出的字符串的首地址,可以是字符數組名或字符指針變量名。//stream 表示向何種流中輸出,可以是標準輸出流 stdout,也可以是文件流。標準輸出流即屏幕輸出,printf 其實也是向標準輸出流中輸出的。int main(){	FILE*pf = fopen("E://c-language-notes//test.21.10.9//data.txt", "w");//以“只寫W”的模式打開文件	if (pf == NULL)	{		perror("fopen");//perror函數顯示錯誤信息		return -1;	}	//寫文件(寫一行)	fputs("hello world/n", pf);	fputs("hello bit/n", pf);	fclose(pf);	pf = NULL;	return 0;}

和fputc差不多,fputc是寫一個字母,fputs是寫一行,運行完上述程序,相關文件夾出現hello world 和hello bit

fgets函數示例:

//char *fgets(char *buf, int bufsize, FILE *stream);//*buf: 字符型指針,指向用來存儲所得數據的地址。//bufsize: 整型數據,指明存儲數據的大小。//*stream: 文件結構體指針,將要讀取的文件流。int main(){	FILE*pf = fopen("E://c-language-notes//test.21.10.9//data.txt", "r");//以“只讀r”的模式打開文件	if (pf == NULL)	{		perror("fopen");//perror函數顯示錯誤信息		return -1;	}	//讀文件(讀一行)	char arr[20] = { 0 };	fgets(arr,5,pf);	printf("%s/n", arr);//打印hell,說是最多讀5個其實是讀到第四個,然后第五個補/0	fclose(pf);	pf = NULL;	return 0;}

我們原先文件里有hello world 和hello bit,我們讀一行中的5個字符到arr里


說是打印5個字符,其實是打印4個字符,第五個字符是自動補/0

4.3格式化輸入輸出函數

格式化輸入輸出也就是按某種格式寫入或讀取
fprintf函數示例:

struct S{	int n;	double d;};int main(){    //int fprintf(FILE *filename, const char *string, . . . .);函數聲明     //看起來函數聲明有點麻煩,我們再來看一下常見的printf函數聲明     //int printf( const char *format, … );     //對比一下很容易發現也就是比printf函數多一個指針參數而已,其他的都按printf來即可	    struct S s = { 100,3.14 };		FILE*pf = fopen("E://c-language-notes//test.21.10.9//data.txt", "w");//以“只寫w”的模式打開文件		if (pf == NULL)		{			perror("fopen");//perror函數顯示錯誤信息			return -1;		}		//寫文件		fprintf(pf,"%d %lf", s.n, s.d);		fclose(pf);		pf = NULL;		return 0;}

fprintf就是printf函數多一個pf指針,其他的和printf都是一樣的,上述代碼運行一下,相關文件出現100,和3.140000(浮點型默認6位小數)

fscanf函數示例:

struct S{	int n;	double d;};int main(){    //int fscanf(FILE *stream, char *format,[argument...]);函數聲明    //和fprintf一樣,就是scanf函數前面多一個pf指針參數	struct S s = { 0};	FILE*pf = fopen("E://c-language-notes//test.21.10.9//data.txt", "r");//以“只讀r”的模式打開文件	if (pf == NULL)	{		perror("fopen");//perror函數顯示錯誤信息		return -1;	}	//讀文件	fscanf(pf,"%d %lf", &(s.n), &(s.d));	printf("%d %lf/n", s.n, s.d);	fclose(pf);	pf = NULL;	return 0;}


原先文件里有100 3.140000,運行程序后讀取出這兩個數

4.4二進制輸入輸出函數

fwrite函數聲明如下
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
ptr-- 這是指向要被寫入的元素數組的指針。
size-- 這是要被寫入的每個元素的大小,以字節為單位。
nmemb-- 這是元素的個數,每個元素的大小為 size 字節。
stream-- 這是指向 FILE 對象的指針,該 FILE 對象指定了一個輸出流。
fwrite函數示例:

//size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)//ptr-- 這是指向要被寫入的元素數組的指針。//size-- 這是要被寫入的每個元素的大小,以字節為單位。//nmemb-- 這是元素的個數,每個元素的大小為 size 字節。//stream-- 這是指向 FILE 對象的指針,該 FILE 對象指定了一個輸出流。struct S{	int n;	double d;	char name[20];};int main(){	    struct S s = { 100,3.14,"zhangsan" };		FILE*pf = fopen("E://c-language-notes//test.21.10.9//data.txt", "wb");//以“只寫wb”的模式打開文件		if (pf == NULL)		{			perror("fopen");//perror函數顯示錯誤信息			return -1;		}		//寫文件-二進制方式寫		fwrite(&s, sizeof(s), 1, pf);		//關閉文件		fclose(pf);		pf = NULL;		return 0;}

這里要注意的是,我們以二進制fwrite寫入,產生的是二進制文件,所以我們以二進制的wb模式打開,文件里的內容如下

因為是二進制文件,我們直接看是看不懂的,但是我們知道可以二進制讀,也就是下面的fread函數
函數聲明:size_t fread( void *buffer, size_t size, size_t count, FILE *stream )
buffer 是讀取的數據存放的內存的指針(可以是數組,也可以是新開辟的空間,buffer就是一個索引)
size 是每次讀取的字節數
count 是讀取次數
strean 是要讀取的文件的指針
fread函數示例:

struct S{	int n;	double d;	char name[20];};int main(){	struct S s = {0};	FILE*pf = fopen("E://c-language-notes//test.21.10.9//data.txt", "rb");//以“只寫wb”的模式打開文件	//wb	if (pf == NULL)	{		perror("fopen");//perror函數顯示錯誤信息		return -1;	}	//讀文件-二進制方式讀	fread(&s, sizeof(struct S), 1, pf);	//打印	printf("%d %lf %s/n", s.n, s.d, s.name);	fclose(pf);	pf = NULL;	return 0;}


我們現在文件里是這些東西,我們運行程序讀一下

可以讀出之前寫入的東西

五、文件的隨機讀寫

5.1fseek

函數聲明:int fseek(FILE *stream, long offset, int fromwhere);
函數設置文件指針stream的位置。如果執行成功,stream將指向以fromwhere為基準,偏移offset個字節的位置。如果執行失敗(比如offset超過文件自身大小),則不改變stream指向的位置。執行成功返回0,否則返回其他數。

fromwhere有三個值:
SEEK_CUR 文件指針當前位置
SEEK_END 文件的末尾
SEEK_SET 文件的起始

比如a的地址就是文件的起始,f就是文件的末尾,假如我現在fgetc讀了一個,指針從a往后移動一位,指針指向b,那b所在位置就是SEEK_CUR,也就是文件指針當前位置

注意:隨機讀寫不是亂讀,而是想讀哪里讀哪里,比如我想讀文件第三個字母,我就可以直接用隨機讀寫讀取第三個字母。我們仍以上面這個文本文檔為例:
現在我要讀第三個,按照常規的順序讀寫,是有一個指針指向a,然后每讀一個,指針往后移一位,讀第三個要移動2次。fseek函數就是可以快速根據指針的位置和偏移量來定位文件指針,大白話講就是fseek函數可以快速找到第三個字母的指針。

#define _CRT_SECURE_NO_WARNINGS#include//fseek函數int main(){	//1.打開文件	FILE*pf=fopen("E://c-language-notes//test.21.10.11//data.txt", "r");	if (pf == NULL)	{		perror("fopen");		return -1;	}	//2.讀文件(隨機讀寫)	//讀c	fseek(pf, 2, SEEK_SET);//現在我要讀c,剛開始cur和set都是起始位置,用cur也可	int ch = fgetc(pf);	printf("%c/n", ch);	//讀b	fseek(pf, -2, SEEK_CUR);	ch = fgetc(pf);	printf("%c/n", ch);	//3.關閉文件	return 0;}

關于讀b,因為我們讀c之后,指針會自動往后移一位,所以cur是指向d的,b關于d的偏移量是-2,所以我們用fseek(pf, -2, SEEK_CUR);讀取

5.2ftell

函數聲明:long int ftell(FILE*filenname);

返回文件指針相對起始位置的偏移量

#include//fseek函數int main(){	//1.打開文件	FILE*pf=fopen("E://c-language-notes//test.21.10.11//data.txt", "r");	if (pf == NULL)	{		perror("fopen");		return -1;	}	//2.讀文件(隨機讀寫)	//讀c	fseek(pf, 2, SEEK_SET);//現在我要讀c,剛開始cur和set都是起始位置,用cur也可	int ch = fgetc(pf);	printf("%c/n", ch);	//讀b	fseek(pf, -2, SEEK_CUR);	ch = fgetc(pf);	printf("%c/n", ch);	int a=ftell(pf);//b讀完之后指針自動往后一位到c,c相對a偏移量為2	printf("%d", a);//打印2	//3.關閉文件	return 0;}

繼上一段代碼,我們讀完b之后指針自動往后移動一位指向c,c相對起始位置a的偏移量為2,所以ftell會返回2

5.3rewind

函數聲明:void rewind(FILE *stream);
不管pf現在在什么位置,傳過去,pf重新指向文件起始位置

六、文本文件和二進制文件

根據數據的組織形式,數據文件被稱為文本文件或者二進制文件
數據在內存中以二進制的形式存儲,如果不加轉換的輸出到外存,就是二進制文件

如果要求在外存上以ASCII碼的形式存儲,則需要在存儲前進行轉換。以ASCII碼的形式存儲的文件就是文本文件

一個數據在內存中是怎么存儲的呢?
字符一律以ASCII碼的形式進行存儲,數值型的數據即可用ASCII碼的形式存儲,也可以用二進制形式存儲,如下,我們進行10000的存儲

(圖片來自比特就業課)
如果我們按ASCII形式存儲,把10000共5位,我們把每位上的數字看做一個字符,共要5個字節

如果我們直接按二進制形式進行存儲,二進制的10000,是
00000000 00000000 00100111 00010000共需占4個字節(1個整形)

七、文件讀取結束的判斷

7.1被錯誤使用的feof

牢記:在文件讀取過程中,不能使用feof函數的返回值直接來判斷文件的結束與否,而是應用于當文件讀取結束的時候,判斷是讀取失敗結束,還是遇到文件尾結束

1.文本文件讀取是否結束,判斷返回值是否為EOF(fgetc),或者NULL(fgets)

*fgetc判斷是否為EOF
fgetc讀到一個字符返回int,如果文件結束沒讀到或者遇到錯誤,返回EOF

原文件里有abcdef5個字符,現在我們怎么利用fgetc進行打印,并判斷是否結束呢?

代碼如下:

#includeint main(){	    //打開文件		FILE*pf=fopen("E://c-language-notes//test.21.10.11//data.txt", "r");		if (pf == NULL)		{			perror("fopen");			return -1;		}
            
                     
             
               

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/122562.html

相關文章

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<