目錄

1.  對文件的介紹

1.1  文件的作用

1.2  程序文件、數據文件

1.3  文件名

2.  二進制文件和文本文件

3.  文件的打開、關閉

3.1  流、標準流

3.2  文件指針

3.3  文件的打開、關閉

3.3.1  fopen函數

3.2.2  fclose函數

4.  文件的順序讀寫(初)

4.1  fputc 函數

 4.2  fgetc 函數(一次性輸入完數據,再回車輸出)

4.3  feof、ferror 函數

4.4  fputs函數

4.5  fgets 函數


1.  對文件的介紹

1.1  文件的作用

--程序的數據存儲在電腦的內存中,若程序退出,內存回收,數據會丟失,再次運行程序什麼也沒有。所以,數據存儲在文件中,就可以持久保存。

--什麼是文件?:磁盤(硬盤)上的文件就是文件。在程序設計中,文件一般分為兩種:程序文件、數據文件(文件功能不同)。

1.2  程序文件、數據文件

  • 程序文件

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

  • 數據文件

文件的內容不一定是程序,而是程序運行時讀寫的數據。比如程序運行需要從中讀取數據的文件,或者輸出內容的文件。

--主要介紹數據文件,前面學習所處理數據的輸入輸出是以終端為對象,即從終端鍵盤輸入數據,屏幕顯示結果。

--有時會將信息輸出到硬盤上,需要時再讀取到內存中使用,這裏處理的是磁盤上的文件。

C語言文件操作 C語言入門到入土(進階篇)(一)_二進制文件

1.3  文件名

--文件有自己的標識,便於進行識別、利用。

文件名包含3部分:文件路徑+文件名主幹+文件後綴

如:C:\code\test.txt

--C:\code\ ->文件路徑;test ->文件名主幹;.txt ->文件後綴;


2.  二進制文件和文本文件

--由數據的組織形式,數據文件被分為文本文件和二進制文件。

  • 二進制文件:數據在內存中以⼆進制形式存儲,不加轉換的輸出到外存的文件中;
  • 文本文件:要求在外存上以ASCII碼的形式存儲,則需要在存儲前轉換。以ASCII字符的形式存儲的文件;

(外存:硬盤、U盤...)

--數據在文件中怎樣存儲?

  • 字符以ASCII形式存儲,數值型數據既可用ASCII形式存儲,也可用二進制形式存儲;
  • 整數10000,以ASCII碼的形式輸出到磁盤,則磁盤中佔用5個字節(每個字符⼀個字節),而二進制形式輸出,則在磁盤上只佔4個字節(int型);

C語言文件操作 C語言入門到入土(進階篇)(一)_二進制文件_02

--代碼演示:(相關操作會在後面介紹)

int main()
{
	int a = 10000;
	FILE* pf = fopen("data.txt", "wb");//打開文件的操作
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fwrite(&a, 4, 1, pf);//將10000以二進制形式寫到文件中
	//關閉文件
	fclose(pf);
	pf = NULL;
	return 0;
}

--想要觀察二進制,在vs上打開:

        --運行結束會創建一個文件->

C語言文件操作 C語言入門到入土(進階篇)(一)_二進制文件_03

        --在vs上添加test文本文件、打開:

C語言文件操作 C語言入門到入土(進階篇)(一)_二進制文件_04

C語言文件操作 C語言入門到入土(進階篇)(一)_二進制文件_05


3.  文件的打開、關閉

3.1  流、標準流

  • 流:

--程序數據需要輸出到外部設備,也要獲取數據,不同外部設備的輸入輸出操作不同,為方便進行操作,抽象出了->"流"的概念,將”流“想成流淌字符的河。
--C語言對文件、畫面、鍵盤等數據的輸入輸出操作都是由”流“完成的。一般,向”流“裏寫數據或讀數據,都要打開”流“,在操作。

C語言文件操作 C語言入門到入土(進階篇)(一)_二進制文件_06

  • 標準流:

--為什麼從鍵盤輸入、屏幕輸出,我們沒有打開”流“?

  --C語言在程序啓動時默認打開3個”流“:

  • stdin - 標準輸入流:在大多數的環境中從鍵盤輸入,scanf函數就是從標準輸入流中讀取數據;
  • stdout - 標準輸出流:大多數的環境中輸出至顯示器界面,printf函數就是將信息輸出到標準輸出流中;
  • stderr - 標準錯誤流,大多數環境中輸出到顯示器界面;

--3個流的類型是 -> FILE * ,通常稱為文件指針。

3.2  文件指針

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

--每個被使用的文件都在內存中開闢相應文件信息區,存放文件相關信息(文件名、文件狀態、文件當前位置...)。這些信息保存在結構體變量中,類型由系統聲明的取名為:FILE。

        --vs2013中提供的 stdio.h 頭文件中有以下的文件類型申明:

struct _iobuf
{
    char* _ptr;
    int _cnt;
    char* _base;
    int _flag;
    int _file;
    int _charbuf;
    int _bufsiz;
    char* _tmpfname;
};
typedef struct _iobuf FILE;

--不同編譯器FILE類型包含內容不完全相同,每次打開文件,系統會自動創建FILE結構的變量,並填充信息,不需要我們關心。

--一般是通過一個FILE指針來維護結構變量:

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

通過文件指針變量能夠間接找到與它關聯的文件。

C語言文件操作 C語言入門到入土(進階篇)(一)_打開文件_07

3.3  文件的打開、關閉

--對於文件,讀寫前要打開,使用結束要關閉:使用  fopen 函數打開文件, fclose 關閉文件。

        --編寫程序的時,打開文件同時,會返回⼀個FILE*的指針變量指向該文件,相當於建立了指針和文件的關係。

        --頭文件:<stdio.h>

3.3.1  fopen函數

FILE* fopen (const char* filename, const char* mode);

功能:

        用來打開參數 filename 所指定文件,同時將其和一個流進行關聯,後續對流的操作是通關 fopen 函數返回的指針來維護的。具體對流的操作是通過參數mode指定的;

參數:

--filename:要打開的文件的路徑和名稱。可以是相對路徑(如 "data.txt"),也可以是絕對路徑(如 "C:\\Users\\data.txt");

(注意:在字符串中使用'\'時,需要用轉義字符,寫兩個'\\'。)

--mode:文件的打開模式是一個字符串,指定文件是用於讀、寫、追加,以及是以文本模式還是二進制模式打開。這是 fopen 的核心參數,決定了能對文件進行何種操作;

返回值:一個 FILE* 類型(文件指針)

  • 文件打開成功,返回指向 FILE 結構的指針。可以用於後續操作中標識對應的流;
  • 文件打開失敗,返回NULL,要對fopen的返回值進行判斷,來驗證文件是否成功打開;

--mode--文件操作方式:

文件使用方式

含義

若指定文件不存在

"r"(只讀)

為了輸入數據,打開已經存在的文本文件

出錯

"w"(只寫)

為了輸出數據,打開文本文件

建立新的文件

“a”(追加)

向文本文件尾添加數據

建立新的文件

“rb”(只讀)

為了輸入數據,打開二進制文件

出錯

"wb"(只寫)

為了輸出數據,打開二進制文件

建立新的文件

“ab”(追加)

向二進制文件尾添加數據

建立新的文件

“r+”(讀寫)

為了讀和寫,打開文本文件

出錯

“w+”(讀寫)

為了讀和寫 建立新的文件

建立新的文件

“a+”(讀寫)

打開文件,在文件尾進行讀寫

建立新的文件

“rb+”(讀寫)

為了讀和寫打開二進制文件

出錯

“wb+”(讀

寫)

為了讀和寫,新建新的二進制文件

建立新的文件

“ab+”(讀

寫)

打開二進制文件,在文件尾進行讀和寫

建立新的文件

--演示幾種方式,體會函數使用:

  •    演示在當前目錄下的操作
int main()
{
	//打開文件,"w"--不存在,創建文件
	FILE* pf = fopen("data.txt", "w");
	//這裏的路徑為相對路徑,表示在當前工程目錄下的data.txt文件
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//後續讀取文件操作
	//……
	//關閉文件
	//……
	return 0;
}

C語言文件操作 C語言入門到入土(進階篇)(一)_數據_08

  •    演示在當前目錄的上一級目中進行打開文件

./ -- 表示當前目錄; ../ --表示當前目錄的上一級目錄

int main()
{
	//打開文件,"r"--不存在,出錯
	FILE* pf = fopen("./../data.txt", "r");//只讀打開,看是否存在
	//這裏的路徑為相對路徑,表示在當前工程目錄上一級目錄下的data.txt文件
	//已經將文件剪切到上一級目錄中
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//後續讀取文件操作
	//……
	//關閉文件
	//……
	return 0;
}
--結果正常運行
  •      演示在當前目錄的上一級目錄的上一級目錄的操作
int main()
{
	//打開文件,"r"--不存在,出錯
	FILE* pf = fopen("./../../data.txt", "r");//只讀打開,看是否存在
	//這裏的路徑為相對路徑,表示在當前工目錄上一級目錄的上一級目錄下的data.txt文件
	//已經將文件剪切到相應目錄中
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//後續讀取文件操作
	//……
	//關閉文件
	//……
	return 0;
}
--結果正常運行
  •      演示當前目錄下上二級目錄中某文件夾中文件夾中的操作
int main()
{
	//打開文件,"r"--不存在,出錯
	FILE* pf = fopen("./../../test/test2/data.txt", "r");//只讀打開,看是否存在
	//這裏的路徑為相對路徑,表示在當前工目錄上二級目錄下的test文件下test2文件的data.txt文件
	//已經將文件剪切到相應目錄中
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//後續讀取文件操作
	//……
	//關閉文件
	//……
	return 0;
}
  •    演示在桌面上的操作
int main()
{
	//打開文件,"r"--不存在,出錯
	FILE* pf = fopen("C:\\Users\\Tian\\Desktop\\data.txt", "r");//只讀打開,看是否存在
	//這裏的路徑為絕對路徑,表示在桌面上的data.txt文件
	//已經將文件剪切到相應目錄中
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//後續讀取文件操作
	//……
	//關閉文件
	//……
	return 0;
}

3.2.2  fclose函數

int fclose(FILE* stream);

功能:

        --關閉參數 stream 關聯的文件,取消關聯關係。與該流關聯的所有內部緩衝區均會解除關聯並刷新:任何未寫入的輸出緩衝區內容將被寫入,任何未讀取的輸入緩衝區內容將被丟棄;

參數:

        --stream: 這是一個指向 FILE 對象的指針,該對象標識要被關閉的流。指針通常由fopen等函數成功調用後返回的;

返回值:成功--返回0、失敗--返回EOF;

--注意:

--必須檢查返回值;

--一個指針關一次:確保每個由 fopen 打開的 FILE* 指針都有且僅有一次對應的 fclose 調用。關閉一個已經關閉的指針或一個空指針會導致未定義行為(程序崩潰);

--作用域問題: 確保指針失效(如局部變量離開作用域)之前關閉文件。否則永遠失去關閉機會,導致資源泄漏(類似於動態內存管理函數的釋放、置空)。

--代碼演示函數功能:

nt main()
{
	//打開文件,"w"--不存在,創建文件
	FILE* pf = fopen("data.txt", "w");
	//這裏的路徑為相對路徑,表示在當前工程目錄下的data.txt文件
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//後續讀取文件操作
	//……
	//關閉文件
	fclose(pf);
	pf == NULL;
	return 0;
}

4.  文件的順序讀寫(初)

--文件讀寫會涉及到以下函數:

函數名

功能

適用於

fgetc

從輸入流讀取一個字符

所有輸入流

fputc

向輸出流寫入一個字符

所有輸出流

fgets

從輸出流讀取一個字符

所有輸入流

fputs

向輸出流寫入一個字符

所有輸出流

fscanf

從輸入流讀取帶有格式的數據

所有輸入流

fprintf

向輸出流寫入帶有格式的數據據

所有輸出流

fread

從輸入流讀取一塊數據

文件輸入流

fwrite

從輸出流寫入一塊數據

所有文件流

4.1  fputc 函數

int fputc(int characer, FILE* stream);

功能:將參數 character 指定的字符寫入 stream 指向的輸出流,通常用於向文件或標準輸出流寫入。寫入字符之後,會調整指示器。字符會被寫入流內部位置指示器當前指向的位置,隨後該指示器自動向前移動⼀個位置;

參數:

--character: 要寫入的字符。雖然參數類型是 int ,但函數內部會將其轉換為 unsigned char 後寫入;

--stream :  是⼀個FILE*類型的指針,指向了輸出流(通常是文件流或stdout);

返回值:--成功: 返回寫入的字符(以 int 形式返回);--失敗/錯誤: 返回 EOF(End Of File,通常是 -1)。

int main()
{
	//打開文件
	FILE* ps = fopen("data.txt", "w");
	//判斷返回值
	if (ps == NULL)
	{
		perror("fopen");
		return 1;
	}
	//寫數據--文件流
	fputc('a', ps);
	fputc('b', ps);
	fputc('c', ps);
	//關閉文件
	fclose(ps);
	ps = NULL;
	return 0;
}
//循環輸出一系列字符
int main()
{
	//打開文件
	FILE* ps = fopen("data.txt", "w");
	if (ps == NULL)
	{
		perror("fopen");
		return 1;
	}
	for (int i = 'a'; i <= 'z'; i++)
	{
		fputc(i, ps);
	}
	fclose(ps);
	ps = NULL;
	return 0;
}
int main()
{
	//寫數據--標準輸出流_屏幕
	fputc('a', stdout);
	fputc('b', stdout);
	fputc('c', stdout);
	return 0;
}

 --打開相應文件進行查看輸出:

C語言文件操作 C語言入門到入土(進階篇)(一)_二進制文件_09

 4.2  fgetc 函數(一次性輸入完數據,再回車輸出)

int fgetc(FILE* stream);

功能:從參數 stream 指向的流中讀取一個字符。函數返回的是文件指示器當前指向的字符,讀取這個字符之後,文件指示器自動前進道下⼀個字符;

參數:

        --stream: FILE* 類型的文件指針,可以是 stdin、其他輸入流的指針。是 stdin 就從標準輸入流讀取數據。是文件流指針,就從文件讀取數據;

返回值:

        --成功: 返回讀取到的字符(以 unsigned char 轉換後的 int 形式返回)。

        --失敗/到達文件末尾: 返回 EOF(設置錯誤指示器( ferror )/文件結束指示器( feof ))。

int main()
{
	//打開文件
	FILE* ps = fopen("data.txt", "r");
	//檢查返回值
	if (ps == NULL)
	{
		perror("fgets");
		return 1;
	}
    //在前面fputc函數操作的基礎上,對文件內容進行讀取
	//讀取數據--文件流
	for (int i = 0; i < 10; i++)
	{
		//讀取
		int c = fgetc(ps);
		//將讀取的數據打印在屏幕
		fputc(c, stdout);
	}
	//關閉文件
	fclose(ps);
	ps = NULL;
	return 0;
}
輸出:abcdefghij
int main()
{
	for (int i = 0; i < 4;i++)
	{
		//讀數據--標準輸入流
		int c = fgetc(stdin);
		fputc(c, stdout);
	}
	return 0;
}

--這裏建議一次性輸入完數據後,再回車輸出!

4.3  feof、ferror 函數

int feof(FILE *stream);   // 檢查是否到達文件末尾
int ferror(FILE *stream); // 檢查是否發生文件錯誤
  • 在讀取文件過程中,遇到文件末尾,文件讀取結束。讀取函數會在對應的流上設置文件結束指示符,指示符可以通過 feof 函數檢測。如果 feof 函數檢測到指示符已經被設置,則返回非0的值,沒有設置則返回0;
  • 在讀/寫文件的過程中,發生了讀/寫錯誤,文件讀取結束。讀/寫函數會在對應的流上設置錯誤指示符,錯誤指示符可以通過 ferror 函數檢測到。如果 ferror 函數檢測到錯誤指示符已被設置,則返回非0的值,沒有設置則返回0;
--測試feof函數
int main()
{
	FILE* ps = fopen("data.txt", "r");
	if (ps == NULL)
	{
		perror("fopen");
		return 1;
	}
	//讀文件-文件:abcdef
	int i = 0;
	for (i = 0;i < 10;i++)
	{
		int c = fgetc(ps);
		if (c == EOF)
		{
			if (feof(ps))
			{
				printf("遇到文件末尾了\n");//到f就沒有了
			}
			else if (ferror(ps))
			{
				printf("讀取發生錯誤\n");
			}
		}
		else
		{
			fputc(c, stdout);
		}
	}
	fclose(ps);
	ps = NULL;
	return 0;
}

4.4  fputs函數

int fputs(const char* str, FILE* stream);

功能:將參數 str 指向的字符串寫入到參數 stream 指定的流中(不含結尾空字符 \0 ),用於文件流或標準輸出(stdout);
參數:

        --str : 指針,指向要寫入的字符串(必須以 \0 結尾);

        --stream : FILE* 的指針,指向要寫入字符串的流;

返回值:--成功,返回非負整數;--失敗,返回 EOF設置流的錯誤指示器,用 ferror() 檢查原因;

int main()
{
	//打開文件
	FILE* ps = fopen("data.txt", "w");
	//判斷
	if (ps == NULL)
	{
		perror("fopen");
		return 1;
	}
	//寫數據
	fputs("abcd", ps);
	//關閉文件
	fclose(ps);
	ps = NULL;
	return 0;
}

4.5  fgets 函數

char* fgets(char* str, int num, FILE* stream);

功能:從 stream 指定輸入流中讀取字符串,讀取到換行符、文件末尾(EOF)或到指定字符數(含結尾空字符 \0 ),然後將讀取到的字符串存儲到str指向的空間;

參數:

        --str :指向字符數組的指針,指向的空間存儲讀取的字符串。

        --num :最大讀取字符數(包含結尾的 \0 ,實際最多讀取 num-1 個字符)。

        --stream :輸入流的文件指針(文件流或 stdin )。

返回值:

        --成功,返回 str 指針;

        --在讀取字符時遇到文件末尾,設置文件結束指示器,返回 NULL ,通過 feof()檢測;

        --發生讀取錯誤,設置流錯誤指示器,返回 NULL,通過 ferror() 檢測。

int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//讀文件
	char arr[20] = "xxxxxxxxxxxxxxxx";
	fgets(arr, 10, pf);
	//關閉文件
	fclose(pf);
	pf = NULL;
	return 0;
}

C語言文件操作 C語言入門到入土(進階篇)(一)_二進制文件_10