C語言文件操作實現(xiàn)數(shù)據(jù)持久化(幫你快速了解文件操作函數(shù))
一.了解文件
1.文件主要功能
- 使用文件我們可以將數(shù)據(jù)直接存放在電腦的硬盤上,做到了數(shù)據(jù)的持久化。
- 在以前各章所處理數(shù)據(jù)的輸入輸出都是以終端為對象的,即從終端的鍵盤輸入數(shù)據(jù),運行結果顯示到顯 示器上。
- 其實有時候我們會把信息輸出到磁盤上,當需要的時候再從磁盤上把數(shù)據(jù)讀取到內(nèi)存中使用,這里處理 的就是磁盤上文件。
2.什么是文件
磁盤上的文件是文件。
但是在程序設計中,我們一般談的文件有兩種:程序文件、數(shù)據(jù)文件(從文件功能的角度來分類的)。
2.1程序文件
包括源程序文件(后綴為.c),目標文件(windows環(huán)境后綴為.obj),可執(zhí)行程序(windows環(huán)境 后綴為.exe)。
2.2數(shù)據(jù)文件
文件的內(nèi)容不一定是程序,而是程序運行時讀寫的數(shù)據(jù),比如程序運行需要從中讀取數(shù)據(jù)的文件, 或者輸出內(nèi)容的文件。
2.3 文件名
文件名包含3部分:文件路徑+文件名主干+文件后綴
比如: c:\code\test.txt
二.文件的打開和關閉
以下所有講的函數(shù)頭文件都是:#include <stdio.h>
1.文件指針
一般都是通過一個FILE的指針來維護這個FILE結構的變量,這樣使用起來更加方便。
我們可以創(chuàng)建一個FILE*的指針變量:
FILE* pf;//文件指針變量
每當打開一個文件的時候,系統(tǒng)會根據(jù)文件的情況自動創(chuàng)建一個FILE結構的變量,并填充其中的信息, 使用者不必關心細節(jié)。
例如,VS2013編譯環(huán)境提供的 stdio.h 頭文件中有以下的文件類型申明:

不同的C編譯器的FILE類型包含的內(nèi)容不完全相同,但是大同小異。
2.文件的打開和關閉
1.打開文件函數(shù):

代碼演示:
FILE *pf = fopen("test.txt", "w");文件名:
文件名分為兩種形式:
1.絕對路徑
文件路徑+文件名主干+文件后綴
2.相對路徑
文件名主干+文件后綴(也可以不加后綴)
相對路勁是在當前程序文件的文件夾下,去查找
文件的打開方式:
| 文件使用方式 | 含義 | 如果指定文件不存在 |
| “r”(只讀) | 為了輸入數(shù)據(jù),打開一個已經(jīng)存在的文本文件 | 出錯 |
| “w”(只寫) | 為了輸出數(shù)據(jù),打開一個文本文件 | 建立一個新的文件 |
| “a”(追加) | 向文本文件尾添加數(shù)據(jù) | 建立一個新的文件 |
| “rb”(只讀) | 為了輸入數(shù)據(jù),打開一個二進制文件 | 出錯 |
| “wb”(只寫) | 為了輸出數(shù)據(jù),打開一個二進制文件 | 建立一個新的文件 |
| “ab”(追加) | 向一個二進制文件尾添加數(shù)據(jù) | 出錯 |
| “r+”(讀寫) | 為了讀和寫,打開一個文本文件 | 出錯 |
| “w+”(讀寫) | 為了讀和寫,建議一個新的文件 | 建立一個新的文件 |
| “a+”(讀寫) | 打開一個文件,在文件尾進行讀寫 | 建立一個新的文件 |
| “rb+”(讀寫) | 為了讀和寫打開一個二進制文件 | 出錯 |
| “wb+”(讀寫) | 為了讀和寫,新建一個新的二進制文件 | 建立一個新的文件 |
| “ab+”(讀寫) | 打開一個二進制文件,在文件尾進行讀和寫 | 建立一個新的文件 |
返回:

正確代碼書寫:
//打開文件
FILE *pf = fopen("test.txt", "w");
//文件是有可能打開失敗的,文件名出錯,打開方式問題等
if (NULL == pf)
{
perror("fopen");
return;
}只有打開文件就一定要搭配上判斷
2.關閉文件函數(shù)

代碼演示:
fclose(pf);
正確的代碼規(guī)范:
int main()
{
//打開文件
FILE *pf = fopen("test.txt", "w");
//文件是有可能打開失敗的,文件名出錯,打開方式問題等
if (NULL == pf)
{
perror("fopen");
return;
}
//寫文件
//……
//關閉文件
fclose(pf);
pf = NULL;//防止再次使用此指針
return 0;
}三.順序讀寫文件函數(shù)
- 這里的輸入是指把文件的數(shù)據(jù)輸入到程序內(nèi)存中
- 輸出指的是內(nèi)存中的數(shù)據(jù),輸出到文件中
站在程序內(nèi)存的角度,把文件想成平時打印的cmd黑框就好
| 功能 | 函數(shù)名 | 適用于 |
| 字符輸入函數(shù) | fgetc | 所有輸入流 |
| 字符輸出函數(shù) | fputc | 所有輸出流 |
| 文本行輸入函數(shù) | fgets | 所有輸入流 |
| 文本行輸出函數(shù) | fputs | 所有輸出流 |
| 格式化輸入函數(shù) | fscanf | 所有輸入流 |
| 格式化輸出函數(shù) | fprintf | 所有輸出流 |
| 二進制輸入 | fread | 文件 |
| 二進制輸出 | fwrite | 文件 |
fputc字符輸入函數(shù)
函數(shù)原型:

補充:輸入流是什么 后面講現(xiàn)在你們看一下代碼就懂
函數(shù)代碼:
這是我當前文件下的一些文件,我講利用打開方式自動創(chuàng)建一個文件
運行程序前的文件夾:

代碼:
#include<stdio.h>
int main()
{
//打開文件
FILE* pf = fopen("test.txt", "w");
if (NULL == pf)
{
perror("fopen");
return 1;
}
//寫文件
int i = 0;
for (i = 0; i < 26; i++)
{
fputc('a' + i, pf);//寫入26個字母
}
//關閉文件
fclose(pf);
pf = NULL;
return 0;
}運行程序前的文件夾:

你們可以去看看是不是跟我打開的文件夾名相同,而且已經(jīng)在里面寫入了26個字母:

返回:
函數(shù)正常執(zhí)行返回所寫數(shù)據(jù),字符返回對應ASII碼值
發(fā)生寫入錯誤返回EOF
fgetc字符輸入函數(shù)
函數(shù)原型

返回:

函數(shù)正常執(zhí)行返回所寫數(shù)據(jù),字符返回對應ASII碼值
發(fā)生寫入錯誤返回EOF
代碼:
我會讀取剛剛fgetc輸入到文件中的數(shù)據(jù)
注意:我打開方式的變化
#include<stdio.h>int main(){//打開文件FILE* pf = fopen("test.txt", "r");if (NULL == pf){perror("fopen");return 1;}//讀文件int ch;while ((ch = fgetc(pf)) != EOF){printf("%c ", ch);}//關閉文件fclose(pf);pf = NULL;return 0;}fgetc讀取了一個字符會自動向后移動一位
結果:

fputs文本行輸出函數(shù)
函數(shù)原型:

返回:

代碼:
一次輸出一行
#include<stdio.h>
int main()
{
//打開文件
FILE* pf = fopen("test.txt", "w");
if (NULL == pf)
{
perror("fopen");
return 1;
}
//寫文件
fputs("hallo\n", pf);
fputs("word", pf);
//關閉文件
fclose(pf);
pf = NULL;
return 0;
}結果:

有此可見轉意字符\n等也是可以操作的
補充:因為文件方式的原因會把之前的數(shù)據(jù)覆蓋掉,成為新的數(shù)據(jù)如果想不覆蓋要用追加的方式打開,我現(xiàn)在把打開方式改為追加執(zhí)行
#include<stdio.h>
int main()
{
//打開文件
FILE* pf = fopen("test.txt", "a");//注意我把打開方式改為了追加a
if (NULL == pf)
{
perror("fopen");
return 1;
}
//寫文件
fputs("hallo\n", pf);
fputs("word", pf);
//關閉文件
fclose(pf);
pf = NULL;
return 0;
}結果:

還有一個二進制的追加ab,其余打開方式請自行去查看上面的表格
fgets文本行輸入函數(shù)
函數(shù)原型:

返回:

代碼:
一次輸入一行
#include<stdio.h>
int main()
{
//打開文件
FILE* pf = fopen("test.txt", "r");
if (NULL == pf)
{
perror("fopen");
return 1;
}
//讀文件
char arr[] = "#######################";//用來觀察一下的字符串
fgets(arr, 20, pf);
printf("%s", arr);//注意我這里沒有加\n因為之前的輸入里面是輸入進去了\n的
fgets(arr, 20, pf);
printf("%s", arr);
//關閉文件
fclose(pf);
pf = NULL;
return 0;
}注意我這里沒有加\n因為之前的輸入里面是輸入進去了\n的
結果:


fprintf格式化輸出函數(shù)
函數(shù)原型:

可以發(fā)現(xiàn)只是多了一個參數(shù)其余并沒有變化
返回:

代碼:
格式化輸出數(shù)據(jù)到文件中
#include<stdio.h>
struct S
{
char name[20];
int age;
float scores;
};
int main()
{
//打開文件
struct S s = { "Laoli" ,20 ,95.5f };
FILE* pf = fopen("test.txt", "w");
if (NULL == pf)
{
perror("fopen");
return 1;
}
//寫文件
fprintf(pf, "%s %d %f\n", s.name, s.age, s.scores);
//關閉文件
fclose(pf);
pf = NULL;
return 0;
}這個字符在輸出字符串時遇到空格也會正常輸出
結果:

fscanf格式化輸入函數(shù)
函數(shù)原型:

返回:

代碼:
把剛剛放進文件的數(shù)據(jù)格式化拿出來
#include<stdio.h>
struct S
{
char name[20];
int age;
float scores;
};
int main()
{
//打開文件
struct S s = { 0 };
FILE* pf = fopen("test.txt", "r");
if (NULL == pf)
{
perror("fopen");
return 1;
}
//讀文件
fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.scores));
printf("%s %d %f", s.name, s.age, s.scores);
//關閉文件
fclose(pf);
pf = NULL;
return 0;
}結果:

這個函數(shù)在遇到空格會停止輸入所有項,例如在Laoli中間加上空格:Lao li

fwrite二進制輸出函數(shù)
函數(shù)原型:

這個函數(shù)是以二進制的方式進行輸出的
返回:

代碼:
#include<stdio.h>
struct S
{
char name[20];
int age;
float scores;
};
int main()
{
//打開文件
struct S s = { "Laoli" ,20 ,95.5f };
FILE* pf = fopen("test.txt", "wb");//注意我已經(jīng)改變了文件的打卡方式
if (NULL == pf)
{
perror("fopen");
return 1;
}
//寫文件
fwrite(&s, sizeof(struct S), 2, pf);
//關閉文件
fclose(pf);
pf = NULL;
return注意我已經(jīng)改變了文件的打卡方式
結果:

大家可以看到除了字符串什么都看不懂呀,這個燙燙燙燙燙燙是什么鬼呀,其實是因為二進制的形式存儲的,計算機可以看懂就可以了,也可以利用fread函數(shù)查看內(nèi)容
fread二進制輸入函數(shù)
函數(shù)原型:

返回:

代碼:
讀取剛剛存入文件的數(shù)據(jù),以二進制形式讀取
#include<stdio.h>
struct S
{
char name[20];
int age;
float scores;
};
int main()
{
//打開文件
struct S s = { 0 };
FILE* pf = fopen("test.txt", "rb");
if (NULL == pf)
{
perror("fopen");
return 1;
}
//讀文件
fread(&s, sizeof(struct S), 1, pf);
printf("%s %d %f", s.name, s.age, s.scores);
//關閉文件
fclose(pf);
pf = NULL;
return 0;
}結果:

四. 解析上述的流
以上函數(shù)都是在文件流中拿信息,是需要fopen函數(shù)來打開的文件流
還幾個默認打開的流:

代碼:

五.文件的隨機讀寫
其實這些函數(shù)也并不是隨機的,也是有規(guī)律的,但不再是從開頭到結尾了
fseek
函數(shù)原型:

偏移量(字節(jié)為單位):
向右移動用正數(shù),向左移動用負數(shù)
起始位置設置:

返回:

代碼:
我手動的在文件中添加這樣的數(shù)據(jù):

從末尾移動三位取出數(shù)據(jù):
#include<stdio.h>
int main()
{
//打開文件
FILE* pf = fopen("test.txt", "rb");
if (NULL == pf)
{
perror("fopen");
return 1;
}
fseek(pf, -3, SEEK_END);
int ch = fgetc(pf);
printf("%c\n", ch);
//關閉文件
fclose(pf);
pf = NULL;
return 0;
}ftell
功能:返回文件指針相對于起始位置的偏移量
函數(shù)原型:

返回:
成功返回當前位置

代碼:
我們就用剛剛的代碼加上函數(shù)試一下:
#include<stdio.h>
int main()
{
//打開文件
FILE* pf = fopen("test.txt", "rb");
if (NULL == pf)
{
perror("fopen");
return 1;
}
fseek(pf, -3, SEEK_END);
int ch = fgetc(pf);
printf("%c\n", ch);
int pos = ftell(pf);
printf("%d", pos);
//關閉文件
fclose(pf);
pf = NULL;
return 0;
}結果:

分析:

rewind
功能:讓文件指針的位置回到文件的起始位置
函數(shù)原型:

代碼:
#include<stdio.h>
int main()
{
//打開文件
FILE* pf = fopen("test.txt", "rb");
if (NULL == pf)
{
perror("fopen");
return 1;
}
fseek(pf, -3, SEEK_END);
int ch = fgetc(pf);
printf("%c\n", ch);
rewind(pf);//回到文件的起始位置
int pos = ftell(pf);
printf("%d", pos);
//關閉文件
fclose(pf);
pf = NULL;
return 0;
}結果:

這個函數(shù)非常簡單不做過多介紹了
此片文章設計大量函數(shù),記不住很正常,收藏一波需要的時候來查一查。
總結
到此這篇關于C語言文件操作實現(xiàn)數(shù)據(jù)持久化(幫你快速了解文件操作函數(shù))的文章就介紹到這了,更多相關C語言文件操作數(shù)據(jù)持久化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
windows系統(tǒng)下C++調用matlab程序的方法詳解
這篇文章主要給大家介紹了關于在windows系統(tǒng)下C++調用matlab程序的方法,文中通過示例代碼介紹的非常詳細,對大家學習或者使用C++具有一定的參考學習價值,需要的朋友們下面跟著小編來一起學習學習吧。2017-08-08

