C語言——文件操做

1、 文件的打開和關閉

1.1 文件指針

在C語言中用一個指針變量指向一個文件,這個指針稱爲文件指針。linux

typedef struct
{
	short           level;	//緩衝區"滿"或者"空"的程度 
	unsigned        flags;	//文件狀態標誌 
	char            fd;		//文件描述符
	unsigned char   hold;	//如無緩衝區不讀取字符
	short           bsize;	//緩衝區的大小
	unsigned char   *buffer;//數據緩衝區的位置 
	unsigned        ar;	 //指針,當前的指向 
	unsigned        istemp;	//臨時文件,指示器
	short           token;	//用於有效性的檢查 
}FILE;

FILE是系統使用typedef定義出來的有關文件信息的一種結構體類型,結構中含有文件名、文件狀態和文件當前位置等信息。web

聲明FILE結構體類型的信息包含在頭文件「stdio.h」中,通常設置一個指向FILE類型變量的指針變量,而後經過它來引用這些FILE類型變量。經過文件指針就可對它所指的文件進行各類操做。
在這裏插入圖片描述windows

C語言中有三個特殊的文件指針由系統默認打開,用戶無需定義便可直接使用:svg

  • stdin: 標準輸入,默認爲當前終端(鍵盤),咱們使用的scanf、getchar函數默認今後終端得到數據。
  • stdout:標準輸出,默認爲當前終端(屏幕),咱們使用的printf、puts函數默認輸出信息到此終端。
  • stderr:標準出錯,默認爲當前終端(屏幕),咱們使用的perror函數默認輸出信息到此終端。

10.2.2 文件的打開
任何文件使用以前必須打開:函數

#include <stdio.h>
FILE * fopen(const char * filename, const char * mode);
功能:打開文件
參數:
	filename:須要打開的文件名,根據須要加上路徑
	mode:打開文件的模式設置
返回值:
	成功:文件指針
	失敗:NULL

第一個參數的幾種形式:ui

FILE *fp_passwd = NULL;

//相對路徑:
//打開當前目錄passdw文件:源文件(源程序)所在目錄
FILE *fp_passwd = fopen("passwd.txt", "r");

//打開當前目錄(test)下passwd.txt文件
fp_passwd = fopen(". / test / passwd.txt", "r");

//打開當前目錄上一級目錄(相對當前目錄)passwd.txt文件
fp_passwd = fopen(".. / passwd.txt", "r");
	
//絕對路徑:
//打開C盤test目錄下一個叫passwd.txt文件
fp_passwd = fopen("c://test//passwd.txt","r");

第二個參數的幾種形式(打開文件的方式):this

打開模式 含義
r或rb 以只讀方式打開一個文本文件(不建立文件,若文件不存在則報錯)
w或wb 以寫方式打開文件(若是文件存在則清空文件,文件不存在則建立一個文件)
a或ab 以追加方式打開文件,在末尾添加內容,若文件不存在則建立文件
r+或rb+ 以可讀、可寫的方式打開文件(不建立新文件)
w+或wb+ 以可讀、可寫的方式打開文件(若是文件存在則清空文件,文件不存在則建立一個文件)
a+或ab+ 以添加方式打開文件,打開文件並在末尾更改文件,若文件不存在則建立文件

注意:spa

  • b是二進制模式的意思,b只是在Windows有效,在Linux用r和rb的結果是同樣的
  • Unix和Linux下全部的文本文件行都是\n結尾,而Windows全部的文本文件行都是\r\n結尾
  • 在Windows平臺下,以「文本」方式打開文件,不加b:
  • 當讀取文件的時候,系統會將全部的 「\r\n」 轉換成 「\n」
  • 當寫入文件的時候,系統會將 「\n」 轉換成 「\r\n」 寫入
  • 以"二進制"方式打開文件,則讀\寫都不會進行這樣的轉換
  • 在Unix/Linux平臺下,「文本」與「二進制」模式沒有區別,"\r\n" 做爲兩個字符原樣輸入輸出
int main(void)
{
	FILE *fp = NULL;

	// "\\"這樣的路徑形式,只能在windows使用
	// "/"這樣的路徑形式,windows和linux平臺下均可用,建議使用這種
	// 路徑能夠是相對路徑,也但是絕對路徑
	fp = fopen("../test", "w");
	//fp = fopen("..\\test", "w");

	if (fp == NULL) //返回空,說明打開失敗
	{
		//perror()是標準出錯打印函數,能打印調用庫函數出錯緣由
		perror("open");
		return -1;
	}

	return 0;
}

1.3 文件的關閉

任何文件在使用後應該關閉:操作系統

  • 打開的文件會佔用內存資源,若是老是打開不關閉,會消耗不少內存
  • 一個進程同時打開的文件數是有限制的,超過最大同時打開文件數,再次調用fopen打開文件會失敗
  • 若是沒有明確的調用fclose關閉打開的文件,那麼程序在退出的時候,操做系統會統一關閉。
#include <stdio.h>
int fclose(FILE * stream);
功能:關閉先前fopen()打開的文件。此動做讓緩衝區的數據寫入文件中,並釋放系統所提供的文件資源。
參數:
	stream:文件指針
返回值:
	成功:0
	失敗:-1
FILE * fp = NULL;
fp = fopen("abc.txt", "r");
fclose(fp);

2、 文件的順序讀寫

2.1 按照字符讀寫文件fgetc、fputc

1)寫文件指針

#include <stdio.h>
int fputc(int ch, FILE * stream);
功能:將ch轉換爲unsigned char後寫入stream指定的文件中
參數:
	ch:須要寫入文件的字符
	stream:文件指針
返回值:
	成功:成功寫入文件的字符
	失敗:返回-1
char buf[] = "this is a test for fputc";
int i = 0;
int n = strlen(buf);
for (i = 0; i < n; i++)
{
	//往文件fp寫入字符buf[i]
	int ch = fputc(buf[i], fp);
	printf("ch = %c\n", ch);
}

2)文件結尾
在C語言中,EOF表示文件結束符(end of file)。在while循環中以EOF做爲文件結束標誌,這種以EOF做爲文件結束標誌的文件,必須是文本文件。在文本文件中,數據都是以字符的ASCII代碼值的形式存放。咱們知道,ASCII代碼值的範圍是0~127,不可能出現-1,所以能夠用EOF做爲文件結束標誌。

#define EOF (-1)

當把數據以二進制形式存放到文件中時,就會有-1值的出現,所以不能採用EOF做爲二進制文件的結束標誌。爲解決這一個問題,ANSI C提供一個feof函數,用來判斷文件是否結束。feof函數既可用以判斷二進制文件又可用以判斷文本文件。

#include <stdio.h>
int feof(FILE * stream);
功能:檢測是否讀取到了文件結尾。判斷的是最後一次「讀操做的內容」,不是當前位置內容(上一個內容)。
參數:
	stream:文件指針
返回值:
	非0值:已經到文件結尾
	0:沒有到文件結尾

3)讀文件

#include <stdio.h>
int fgetc(FILE * stream);
功能:從stream指定的文件中讀取一個字符
參數:
	stream:文件指針
返回值:
	成功:返回讀取到的字符
	失敗:-1
char ch;
#if 0
while ((ch = fgetc(fp)) != EOF)
{
	printf("%c", ch);
}
printf("\n");
#endif

while (!feof(fp)) //文件沒有結束,則執行循環
{
	ch = fgetc(fp);
	printf("%c", ch);
}
printf("\n");

2.2按照行讀寫文件fgets、fputs

1)寫文件

#include <stdio.h>
int fputs(const char * str, FILE * stream);
功能:將str所指定的字符串寫入到stream指定的文件中,字符串結束符 '\0'  不寫入文件。 
參數:
	str:字符串
	stream:文件指針
返回值:
	成功:0
	失敗:-1
char *buf[] = { "123456\n", "bbbbbbbbbb\n", "ccccccccccc\n" };
int i = 0;
int n = 3;
for (i = 0; i < n; i++)
{
	int len = fputs(buf[i], fp);
	printf("len = %d\n", len);
}

2)讀文件

#include <stdio.h>
char * fgets(char * str, int size, FILE * stream);
功能:從stream指定的文件內讀入字符,保存到str所指定的內存空間,直到出現換行字符、讀到文件結尾或是已讀了size - 1個字符爲止,最後會自動加上字符 '\0' 做爲字符串結束。
參數:
	str:字符串
	size:指定最大讀取字符串的長度(size - 1)
	stream:文件指針
返回值:
	成功:成功讀取的字符串
	讀到文件尾或出錯: NULL
char buf[100] = 0;

while (!feof(fp)) //文件沒有結束
{
	memset(buf, 0, sizeof(buf));
	char *p = fgets(buf, sizeof(buf), fp);
	if (p != NULL)
	{
		printf("buf = %s", buf);
	}
}

2.3按照格式化文件fprintf、fscanf

1)寫文件

#include <stdio.h>
int fprintf(FILE * stream, const char * format, ...);
功能:根據參數format字符串來轉換並格式化數據,而後將結果輸出到stream指定的文件中,指定出現字符串結束符 '\0'  爲止。
參數:
	stream:已經打開的文件
	format:字符串格式,用法和printf()同樣
返回值:
	成功:實際寫入文件的字符個數
	失敗:-1

fprintf(fp, "%d %d %d\n", 1, 2, 3);

2)讀文件

#include <stdio.h>
int fscanf(FILE * stream, const char * format, ...);
功能:從stream指定的文件讀取字符串,並根據參數format字符串來轉換並格式化數據。
參數:
	stream:已經打開的文件
	format:字符串格式,用法和scanf()同樣
返回值:
	成功:參數數目,成功轉換的值的個數
	失敗: - 1
int a = 0;
int b = 0;
int c = 0;
fscanf(fp, "%d %d %d\n", &a, &b, &c);
printf("a = %d, b = %d, c = %d\n", a, b, c);

2.4按照塊讀寫文件fread、fwrite

1)寫文件

#include <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:以數據塊的方式給文件寫入內容
參數:
	ptr:準備寫入文件數據的地址
	size: size_t 爲 unsigned int類型,此參數指定寫入文件內容的塊數據大小
	nmemb:寫入文件的塊數,寫入文件數據總大小爲:size * nmemb
	stream:已經打開的文件指針
返回值:
	成功:實際成功寫入文件數據的塊數目,此值和nmemb相等
	失敗:0
typedef struct Stu
{
	char name[50];
	int id;
}Stu;

Stu s[3];
int i = 0;
for (i = 0; i < 3; i++)
{
	sprintf(s[i].name, "stu%d%d%d", i, i, i);
	s[i].id = i + 1;
}

int ret = fwrite(s, sizeof(Stu), 3, fp);
printf("ret = %d\n", ret);

2)讀文件

#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:以數據塊的方式從文件中讀取內容
參數:
	ptr:存放讀取出來數據的內存空間
	size: size_t 爲 unsigned int類型,此參數指定讀取文件內容的塊數據大小
	nmemb:讀取文件的塊數,讀取文件數據總大小爲:size * nmemb
	stream:已經打開的文件指針
返回值:
	成功:實際成功讀取到內容的塊數,若是此值比nmemb小,但大於0,說明讀到文件的結尾。
	失敗:0
typedef struct Stu
{
	char name[50];
	int id;
}Stu;

Stu s[3];
int ret = fread(s, sizeof(Stu), 3, fp);
printf("ret = %d\n", ret);

int i = 0;
for (i = 0; i < 3; i++)
{
	printf("s = %s, %d\n", s[i].name, s[i].id);
}

3、 文件的隨機讀寫

#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
功能:移動文件流(文件光標)的讀寫位置。
參數:
	stream:已經打開的文件指針
	offset:根據whence來移動的位移數(偏移量),能夠是正數,也能夠負數,若是正數,則相對於whence往右移動,若是是負數,則相對於whence往左移動。若是向前移動的字節數超過了文件開頭則出錯返回,若是向後移動的字節數超過了文件末尾,再次寫入時將增大文件尺寸。
	whence:其取值以下:
		SEEK_SET:從文件開頭移動offset個字節
		SEEK_CUR:從當前位置移動offset個字節
		SEEK_END:從文件末尾移動offset個字節
返回值:
	成功:0
	失敗:-1

#include <stdio.h>
long ftell(FILE *stream);
功能:獲取文件流(文件光標)的讀寫位置。
參數:
	stream:已經打開的文件指針
返回值:
	成功:當前文件流(文件光標)的讀寫位置
	失敗:-1

#include <stdio.h>
void rewind(FILE *stream);
功能:把文件流(文件光標)的讀寫位置移動到文件開頭。
參數:
	stream:已經打開的文件指針
返回值:
	無返回值
typedef struct Stu
{
	char name[50];
	int id;
}Stu;

//假如已經往文件寫入3個結構體
//fwrite(s, sizeof(Stu), 3, fp);

Stu s[3];
Stu tmp; 
int ret = 0;

//文件光標讀寫位置從開頭往右移動2個結構體的位置
fseek(fp, 2 * sizeof(Stu), SEEK_SET);

//讀第3個結構體
ret = fread(&tmp, sizeof(Stu), 1, fp);
if (ret == 1)
{
	printf("[tmp]%s, %d\n", tmp.name, tmp.id);
}

//把文件光標移動到文件開頭
//fseek(fp, 0, SEEK_SET);
rewind(fp);

ret = fread(s, sizeof(Stu), 3, fp);
printf("ret = %d\n", ret);

int i = 0;
for (i = 0; i < 3; i++)
{
	printf("s === %s, %d\n", s[i].name, s[i].id);
}

4、 Windows和Linux文本文件區別

  • b是二進制模式的意思,b只是在Windows有效,在Linux用r和rb的結果是同樣的
  • Unix和Linux下全部的文本文件行都是\n結尾,而Windows全部的文本文件行都是\r\n結尾
  • 在Windows平臺下,以「文本」方式打開文件,不加b:
  • 當讀取文件的時候,系統會將全部的 「\r\n」 轉換成 「\n」
  • 當寫入文件的時候,系統會將 「\n」 轉換成 「\r\n」 寫入
  • 以"二進制"方式打開文件,則讀\寫都不會進行這樣的轉換
  • 在Unix/Linux平臺下,「文本」與「二進制」模式沒有區別,"\r\n" 做爲兩個字符原樣輸入輸出

判斷文本文件是Linux格式仍是Windows格式:

#include<stdio.h>

int main(int argc, char **args)
{
	if (argc < 2)
		return 0;

	FILE *p = fopen(args[1], "rb");
	if (!p)
		return 0;

	char a[1024] = { 0 };
	fgets(a, sizeof(a), p);

	int len = 0;
	while (a[len])
	{
		if (a[len] == '\n')
		{
			if (a[len - 1] == '\r')
			{
				printf("windows file\n");
			}
			else
			{
				printf("linux file\n");
			}
		}
		len++;
	}

	fclose(p);

	return 0;
}

5、 獲取文件狀態

#include <sys/types.h>
#include <sys/stat.h>
int stat(const char *path, struct stat *buf);
功能:獲取文件狀態信息
參數:
path:文件名
buf:保存文件信息的結構體
返回值:
成功:0
失敗-1

struct stat {
	dev_t         st_dev;         //文件的設備編號
	ino_t         st_ino;          //節點
	mode_t        st_mode;   //文件的類型和存取的權限
	nlink_t       st_nlink;     //連到該文件的硬鏈接數目,剛創建的文件值爲1
	uid_t         st_uid;         //用戶ID
	gid_t         st_gid;         //組ID
	dev_t         st_rdev;      //(設備類型)若此文件爲設備文件,則爲其設備編號
	off_t         st_size;        //文件字節數(文件大小)
	unsigned long st_blksize;   //塊大小(文件系統的I/O 緩衝區大小)
	unsigned long st_blocks;    //塊數
	time_t        st_atime;     //最後一次訪問時間
	time_t        st_mtime;    //最後一次修改時間
	time_t        st_ctime;     //最後一次改變時間(指屬性)
};
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>

int main(int argc, char **args)
{
	if (argc < 2)
		return 0;

	struct stat st = { 0 };

	stat(args[1], &st);
	int size = st.st_size;//獲得結構體中的成員變量
	printf("%d\n", size);
	return 0;
}

6、 刪除文件、重命名文件名

#include <stdio.h>
int remove(const char *pathname);
功能:刪除文件
參數:
	pathname:文件名
返回值:
	成功:0
	失敗:-1

#include <stdio.h>
int rename(const char *oldpath, const char *newpath);
功能:把oldpath的文件名改成newpath
參數:
oldpath:舊文件名
newpath:新文件名
返回值:
成功:0
失敗: - 1

7、 文件緩衝區

7.1文件緩衝區

ANSI C標準採用「緩衝文件系統」處理數據文件。

所謂緩衝文件系統是指系統自動地在內存區爲程序中每個正在使用的文件開闢一個文件緩衝區從內存向磁盤輸出數據必須先送到內存中的緩衝區,裝滿緩衝區後才一塊兒送到磁盤去。

若是從磁盤向計算機讀入數據,則一次從磁盤文件將一批數據輸入到內存緩衝區(充滿緩衝區),而後再從緩衝區逐個地將數據送到程序數據區(給程序變量) 。

7.2磁盤文件的存取

  • 磁盤文件,通常保存在硬盤、U盤等掉電不丟失的磁盤設備中,在須要時調入內存
  • 在內存中對文件進行編輯處理後,保存到磁盤中
  • 程序與磁盤之間交互,不是當即完成,系統或程序可根據須要設置緩衝區,以提升存取效率

7.3更新緩衝區

#include <stdio.h>
int fflush(FILE *stream);
功能:更新緩衝區,讓緩衝區的數據立馬寫到文件中。
參數:
stream:文件指針
返回值:
成功:0
失敗:-1
相關文章
相關標籤/搜索