C語言文件操作
- 一、為什么使用文件?
- 二、文件分類
- 三、文件的打開和關閉
- 四、文件的順序讀寫
- 4.1fputc
- 4.2fgetc
- 4.3fputs
- 4.4fgets
- 4.5 fprintf
- 4.6 fscanf
- 4.7 fwrite
- 4.8 fread
- 五、文件的隨機讀寫
- 5.1 fseek
- 5.2 ftell和rewind
- 六、文件讀取結束的判定
- 七、文件緩沖區
一、為什么使用文件?
我們以創建變量舉例,假設我們創建一個整型變量b,并且我們將它初始化為100,這樣我們寫的數據是儲存在電腦的內存中的,然后我們退出程序,內存回收,數據就丟失了,等到再次運行程序的時候是看不到上一次程序的數據的。那我們能不能把它長久保存下來呢?這就需要用到文件,文件是保存在磁盤上的,使用磁盤(文件)來儲存數據我們就可以對數據進行持久化儲存。
二、文件分類
- 程序文件:包括源程序文件(后綴為.c)目標文件(在windows環境下后綴為.obj)可執行程序(在windows環境下后綴為.exe)。
- 數據文件:文件也不一定都是程序,也有可能文件中存放的是程序運行時讀寫的數據,這就是數據文件。
文件名:一個文件要有一個唯一的文件標識,以便用戶識別和引用。
文件名包含三個部分:文件路徑 + 文件名主干 + 文件后綴
例如:c:\code\test.c 中的c:\code\就是文件路徑,test就是文件名主干,.c就是文件后綴。
二進制文件和文本文件:
根據數據的組織形式,數據被稱為二進制文件和文本文件。
數據在內存中是以二進制的形式存儲的,如果它不加轉換的輸出到外存的文件中,就是二進制文件,如果要求在外存的基礎上以ASCll碼的形式存儲,這時候就需要在儲存前轉換,以ASCll碼形式儲存的文件就是文本文件。
三、文件的打開和關閉
文件在讀之前需要先打開文件,這時候你就需要一枚鑰匙(fopen),在使用之后你還要關閉文件,這時候你需要一把鎖(fclose)。
這兩個函數都在#include <stdio.h>下,接下來我們來講解這兩個函數。
首先來看fopen的函數原型:
FILE* fopen(const char* filename,const char*mode);
//filename就是你要打開的文件名
//mode是你要以什么方式打開,像讀、寫等等
打開完成后fopen會返回一個FILE*類型的指針用于文件操作,如果打開失敗fopen會返回空指針(NULL),所以通常我們需要對fopen的返回值進行判斷。
關于文件的使用方式可以參照以下表格:
文件使用方式 | 含義 | 如果指定文件不存在 |
---|---|---|
“r”(只讀) | 為了輸入數據,打開一個已經存在的文本文件 | 出錯 |
“w”(只寫) | 為了輸出數據,打開一個文本文件 | 建立一個新的文件 |
“a”(追加) | 像文本文件末尾添加數據 | 建立一個新的文件 |
“rb”(只讀) | 為了輸入數據,打開一個二進制文件 | 出錯 |
“wb”(只寫) | 為了輸出數據,打開一個二進制文件 | 建立一個新的文件 |
等等還有很多種操作,這里小編只列舉了這幾種方式來進行解釋。
之后是fclose的函數原型:
int fclose(FILE* stream);
//stream是指向文件的FILE*類型的指針
示例:
首先我不創建text.c的文件:
#include<stdio.h>int main()
{FILE* pf;pf = fopen("text.c", "r");if (pf == NULL){printf("打開失敗");}else{printf("打開成功");}fclose(pf);pf = NULL;return 0;
}
結果:
之后我再創建文件:
結果:
四、文件的順序讀寫
既然我們已經可以打開關閉文件了,那總要做點什么吧,比如說往文件里面寫一些數據,或者讀一些數據,這時候就需要用到我們的函數,首先介紹順序讀寫,之后介紹隨機讀寫。
函數名 | 功能 | 適用于 |
---|---|---|
fgetc | 字符輸入函數 | 所有輸入流 |
fputc | 字符輸出函數 | 所有輸出流 |
fgets | 文本行輸入函數 | 所有輸入流 |
fputs | 文本行輸出函數 | 所有輸出流 |
fscanf | 格式化輸入函數 | 所有輸入流 |
fprintf | 格式化輸出函數 | 所有輸出流 |
fread | 二進制輸入 | 文件輸入流 |
fwrite | 二進制輸出 | 文件輸出流 |
4.1fputc
函數原型:
這個函數的作用是向stream指向的文件中寫東西。
注意:fputc是向文件中寫數據,文件打開方式應該使用"w"
示例:
#include<stdio.h>int main()
{FILE* pf;pf = fopen("text.txt", "w");if (pf == NULL){perror("fopen");return 1;}//寫文件for (int i = 'a';i < 'z';i++){fputc(i, pf);}fclose(pf);pf = NULL;return 0;
}
程序執行前:
程序執行后:
4.2fgetc
函數原型:
fgetc函數的作用是從文件中讀取數據,我們可以把讀取到的數據打印在屏幕上。
注意:此時是從文件中讀取數據是只讀的情況,所以文件打開方式應該選用“r”,當文件中的字符被逐個讀完之后沒有字符可讀,或者一開始就沒有字符可讀時,fgetc函數會返回EOF(-1),所以我們可以以EOF來判斷是否讀取結束。
示例:
#include<stdio.h>int main()
{FILE* pf;pf = fopen("text.txt", "r");if (pf == NULL){perror("fopen");return 1;}//讀文件int c = 0;while ((c = fgetc(pf)) != EOF){printf("%c ", c);}fclose(pf);pf = NULL;return 0;
}
結果:
4.3fputs
函數原型:
注意:向文件中寫入字符串,打開文件方式用“w”
實例:
#include<stdio.h>int main()
{FILE* pf;pf = fopen("text.txt", "w");if (pf == NULL){perror("fopen");return 1;}//寫文件fputs("hello world\n", pf);fputs("hello new world\n",pf);fclose(pf);pf = NULL;return 0;
}
結果:
4.4fgets
函數原型:
示例:
#include<stdio.h>int main()
{FILE* pf;pf = fopen("text.txt", "r");if (pf == NULL){perror("fopen");return 1;}//讀文件char c[101];char s[101];fgets(c, 13, pf);fgets(s, 16, pf);printf("%s", c);printf("%s", s);fclose(pf);pf = NULL;return 0;
}
結果:
使用fgets的注意事項:
1、當你要求它讀取8個字符的時候他其實會從文件中讀取7個字符剩下的那個字符空間他會用來儲存‘\0’。
2、不知道大家有沒有發現我的打印函數里面都沒有添加換行符,但打印出來卻換行了,這是因為fgets讀取到了文件行末尾的換行符。
4.5 fprintf
函數原型:
首先函數的第一個參數是指向文件的FILE*指針,第二個參數是傳入數據的形式,像%d、%f這些。
示例:
#include<stdio.h>int main()
{FILE* pf;pf = fopen("text.txt", "w");if (pf == NULL){perror("fopen");return 1;}//寫文件fprintf(pf, "%d %lf %c", 100, 3.14, 'a');fclose(pf);pf = NULL;return 0;
}
結果:
4.6 fscanf
函數原型:
示例:
#include<stdio.h>int main()
{FILE* pf;pf = fopen("text.txt", "r");if (pf == NULL){perror("fopen");return 1;}int n;double d;char c;//讀文件fscanf(pf, "%d %lf %c", &n,&d,&c);printf("%d\n", n);printf("%f\n", d);printf("%c\n", c);fclose(pf);pf = NULL;return 0;
}
結果:
4.7 fwrite
函數原型:
其中ptr指針指向的是要傳文件數據的數組,size是一次傳送的字節大小,count是傳送的次數,stream是FILE*類型的指針。此時文件的打開方式應該是“wb”。
示例:
#include<stdio.h>int main()
{FILE* pf;pf = fopen("text.txt", "wb");if (pf == NULL){perror("fopen");return 1;}//寫文件int a[] = { 1,2,3 };fwrite(a,sizeof(a[0]),3,pf);fclose(pf);pf = NULL;return 0;
}
結果:
當我們以二進制的形式存入數據的時候,它給出的結果我們已經識別不出來了,我們轉化為2進制再看一下:
以二進制的方式打開我們發現的確儲存了1,2,3,而且是以16進制的形式儲存的和內存的儲存形式相同。
4.8 fread
函數原型:
其中ptr指針指向的是要存放數據的數組,size是從文件讀取的字節大小,count是讀取的個數,stream是FILE*類型的指針。此時文件的打開方式應該是“rb”。
示例:
#include<stdio.h>int main()
{FILE* pf;pf = fopen("text.txt", "rb");if (pf == NULL){perror("fopen");return 1;}//讀文件int arr[4] = { 0 };for (int i = 0;i < 3;i++){fread(arr + i, sizeof(arr[0]), 1, pf);printf("%d ", arr[i]);}fclose(pf);pf = NULL;return 0;
}
結果:
五、文件的隨機讀寫
5.1 fseek
函數原型:
第一個參數是一個FILE*類型的指針,第二個參數是偏移量,第三個參數是參照系。那這個具體是什么意思呢?就是選擇一個參照系,比如我選擇文件開頭,然后偏移量選擇3,那就是從文件中第三個字符開始讀取的意思。
關于參照系這里有一個表格,它一共有三個取值:
SEEK_SET:文件的開始位置
SEEK_CUR:光標的當前位置
SEEK_END:文件末尾位置
示例:
#include<stdio.h>int main()
{FILE* pf;pf = fopen("text.txt", "r");if (pf == NULL){perror("fopen");return 1;}//讀文件char arr[10];fseek(pf, -11, SEEK_END);arr[0] = fgetc(pf);fputc(arr[0], stdout);fclose(pf);pf = NULL;return 0;
}
結果:
另外兩個origin取值也是相同的用法。
5.2 ftell和rewind
ftell的函數原型:
它只有一個參數就是文件指針,它的作用是返回光標當前位置。
rewind的函數原型:
它也只有一個參數就是文件指針,它的作用是讓光標回到文件的起始位置。
示例:
#include<stdio.h>int main()
{FILE* pf;pf = fopen("text.txt", "r");if (pf == NULL){perror("fopen");return 1;}//讀文件char arr[10];fseek(pf, -11, SEEK_END);arr[0] = fgetc(pf);fputc(arr[0], stdout);long int n = ftell(pf);printf("\n%ld\n", n);rewind(pf);n=ftell(pf);printf("%ld", n);fclose(pf);pf = NULL;return 0;
}
結果:
六、文件讀取結束的判定
C語言中有一個函數是feof,它的作用是當文件讀取結束的時候,判斷讀取的原因是不是遇到文件尾而結束。
注意:在文件讀取的過程中不能用feof函數的返回值直接判斷文件是否結束。
它的函數原型:
那如何判斷文件是否讀取結束呢?
對于文本文件
例如:fgetc判斷返回值是不是EOF,fgets判斷返回值是不是NULL。
對于二進制文件
二進制文件是否讀取結束,是判斷返回值是不是小于實際要讀的個數。
例如:fread判斷返回值是否小于實際要讀的個數。
示例:
#include<stdio.h>int main()
{FILE* pf;pf = fopen("text.txt", "r");if (pf == NULL){perror("fopen");return 1;}//讀文件int c;while ((c = fgetc(pf)) != EOF){putchar(c);}if (ferror(pf))//判斷是不是異常結束{printf("\nI/O error when reading");}else if (feof(pf))//判斷是不是正常結束{printf("\nEnd of file reached successfully");}fclose(pf);pf = NULL;return 0;
}
結果:
七、文件緩沖區
ANSI C 標準是采用“緩沖文件系統”處理數據文件的,所謂緩沖文件系統是指系統自動地在內存中為程序中每一個正在使用的文件開辟一塊“文件緩沖區”。從內存向磁盤輸出數據會先送到內存中的緩沖區,裝滿緩沖區后才一起送到磁盤(文件)上。如果從磁盤向計算機讀入數據,則從磁盤文件中讀取數據輸入到內存緩沖區(充滿緩沖區),然后再從緩沖區逐個地將數據送到程序數據區(程序變量等)。緩沖區的大小根據C編譯系統決定的。
例如:
當我們輸入數據時,系統會開辟一個輸入緩沖區,假設這個緩沖區的大小是固定的,我們輸入數據時它會等到緩沖區被充滿后在一起送到磁盤上,輸出緩沖區也是這樣。這樣就大大解放了程序的空間和時間,使計算機能夠同時處理更多的事。
總結
以上就是全部的今天博客內容,內容稍微有點多,讀者朋友可以點個關注,博主后續還會給大家帶來更多的優質內容~
另外,如果有什么問題,可以在評論區留言,博主看到后,會一一回復。
好了,讓我們下期博客再見!
(~ ̄▽ ̄)~