C語言檔案讀寫
檔案分類:
二進位制檔案:把資料的補碼直接寫入檔案,這種檔案叫二進位制檔案。
優點:讀寫和寫入時不需要進行轉換,所以讀寫速度快,資料安全性高。
缺點:不能使用文字編譯器開啟,無法閱讀。
文字檔案:把資料轉換成字串寫入檔案,也就是把字元的二進位制寫入檔案,這種檔案叫文字檔案。
優點:能被文字編輯器開啟,人類能看的懂,能夠看出資料是否出錯。
缺點:讀寫時需要轉換,讀寫速度慢,資料有被修改的風險。
int num = 12306;
char ch = 'A';
二進位制檔案中的內容:
00000000 00000000 00110000 00010010
文字檔案中儲存的內容:
先把num的值轉換成字串"12306" 再把字串中的每個字元的ASCII值的二進位制寫入文字檔案
00110001 00110010 00110011 00110000 00110110
總結:二進位制檔案的大小是確定的,文字檔案會根據內容而變化
開啟/關閉檔案:
FILE *fopen(const char *path, const char *mode);
功能:開啟檔案
path:檔案的路徑
mode:檔案的開啟模式
返回值:檔案結構指標,是後續操作檔案的憑證,失敗則返回NULL。
int fclose(FILE *stream);
功能:關閉檔案
返回值:成功返回0,失敗返回-1。
注意:不能重複關閉,否則會出現double free錯誤,為了避免出現這種錯誤,在檔案關閉及時把檔案指標賦值為NULL,及時關閉檔案可以把緩衝區中的資料寫入到檔案中。
檔案的開啟模式:
"r" 以只讀方式開啟文字檔案,如果檔案不存在,或檔案沒有讀許可權則開啟失敗。
"r+" 在"r"的基礎上增加了寫許可權。
"w" 以只寫方式開啟文字檔案,如果檔案不存在則建立,如果檔案存在則清空檔案的內容,如果檔案存在但沒有寫許可權,則開啟失敗。
"w+" 在"w"的基礎上增加了讀許可權。
"a" 以只寫方式開啟文字檔案,如果檔案不存在則建立,如果檔案存在則新寫入的內容追加到檔案末尾,如果檔案存在但沒有寫許可權,則開啟失敗。
"a+" 在"a"的基礎上增加了讀許可權。
如果要操作二進位制檔案,則在以上模式的基礎上增加b。
檔案的文字開啟方式和二進位制開啟方式的區別:
在 UNIX/Linux 平臺中,用文字方式或二進位制方式開啟檔案沒有任何區別。
在 UNIX/Linux 平臺中,文字檔案以\n
(ASCII 碼為 0x0a)作為換行符號;而在 Windows 平臺中,文字檔案以連在一起的\r\n
(\r
的 ASCII 碼是 0x0d)作為換行符號。
在 Windows 平臺中,如果以文字方式開啟檔案,當讀取檔案時,系統會將檔案中所有的\r\n
轉換成一個字元\n
,如果檔案中有連續的兩個位元組是 0x0d0a,則系統會丟棄前面的 0x0d 這個位元組,只讀入 0x0a。當寫入檔案時,系統會將\n
轉換成\r\n
寫入。
也就是說,如果要寫入的內容中有位元組為 0x0a,則在寫入該位元組前,系統會自動先寫入一個 0x0d。因此,如果用文字方式開啟二進位制檔案進行讀寫,讀寫的內容就可能和檔案的內容有出入。
因此,用二進位制方式開啟檔案總是最保險的。
文字檔案的讀寫:
int fprintf(FILE *stream, const char *format, ...);
功能:把若干個變數以文字格式寫入到指定的檔案中
stream:要寫入的檔案憑證,必須是fopen的返回值。
format:佔位符+跳脫字元+提示資訊
...:若干個變數
返回值:寫入字元的數量
int fscanf(FILE *stream, const char *format, ...);
功能:從檔案中讀取資料
stream:要讀取的檔案
format:佔位符
...:若干個變數的地址
返回值:成功讀取的變數個數
1、設計一個圖書結構體(書名、版本號、作者、出版社),定義一個圖書結構變數並初始化,以文字方式寫入book.txt檔案中
#include <stdio.h>
typedef struct Date {
char ch;
short sh;
int in;
double f;
} Date;
int main() {
Date d = {'a', 33, 889988, 3.1415926};
FILE* fwp = fopen("test.txt", "w");
if (NULL == fwp){
perror("fopen");
return -1;
}
printf("檔案開啟成功\n");
int ret = fprintf(fwp, "第一個%c %hd %d %lf\n", d.ch, d.sh, d.in, d.f);
printf("ret = %d\n", ret);
}
2、從book.txt中讀取一條資料到圖書結構變數中
#include <stdio.h>
#include <stdlib.h>
typedef struct Date {
char ch;
short sh;
int in;
double f;
} Date;
int main() {
FILE* frp = fopen("test.txt", "r");
if(NULL == frp) {
perror("fopen");
return -1;
}
printf("檔案開啟成功\n");
Date* p = malloc(sizeof(Date));
int ret = fscanf(frp, "第一個%c %hd %d %lf", &p->ch, &p->sh, &p->in, &p->f);
printf("ret=%d %c %hd %d %lf\n", ret, p->ch, p->sh+88, p->in, p->f);
}
給通訊錄專案增加資料持久化功能(載入資料、儲存資料)
二進位制檔案的讀寫:
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
功能:把一塊記憶體當作陣列,然後陣列中的內容以二進位制格式寫入到檔案中
ptr:陣列首地址
size:陣列元素的位元組數
nmemb:陣列的長度
stream:要寫入的檔案
返回值:實際寫入的次數
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:把二進位制檔案中的內容讀取的陣列中
ptr:要儲存資料的陣列首地址
size:陣列元素的位元組數
nmemb:陣列的容量
返回值:成功讀取的次數
注意:以二進位制格式讀寫檔案時,最好加上mode最好包含b。
#include <stdio.h>
#include <stdlib.h>
#define N 15
int arr[N];
int main() {
for (int i = 0; i < N; ++i) {
arr[i] = rand() % 100;
}
FILE* fwp = fopen("arr.bin", "wb");
if(NULL == fwp) {
perror("fopen");
return -1;
}
int ret = fwrite(arr, sizeof(arr[0]), N, fwp);
printf("成功寫入%d個元素\n",ret);
fclose(fwp);
fwp = NULL;
}
#include <stdio.h>
#include <stdlib.h>
#define N 15
int main() {
FILE* frp = fopen("arr.bin", "rb");
if(NULL == frp) {
perror("fopen");
return -1;
}
int* p = malloc(sizeof(p[0]) * N);
if(NULL == p) {
printf("p is NULL\n");
return 0;
}
int ret = fread(p, sizeof(p[0]), N, frp);
printf("ret=%d\n", ret);
for (int i = 0; i < N; ++i) {
printf("%d%c", p[i], " \n"[i == N - 1]);
}
fclose(frp);
frp = NULL;
}
注意:
如果以fwrite/fread讀寫的字元陣列,那麼我們操作的依然是文字檔案。
#include <stdio.h>
#include <stdlib.h>
#define N 15
char arr[N];
int main(int argc,const char* argv[]) {
for (int i = 0; i < N; ++i) {
arr[i] = rand() % 26 + 65;
}
FILE* fwp = fopen("arr.bin", "wb");
if (NULL == fwp) {
perror("fopen");
return -1;
}
int ret = fwrite(arr, sizeof(arr[0]), N, fwp);
printf("成功寫入%d個元素\n", ret);
fclose(fwp);
fwp = NULL;
}
int sprintf(char *str, const char *format, ...);
功能:把若干個變數轉換成字串輸出到str陣列中
int sscanf(const char *str, const char *format, ...);
功能:從字串讀取若干個變數
#include <stdio.h>
#include <string.h>
typedef struct Data {
char ch;
short sh;
int i;
double d;
} Data;
int main() {
Data d = {88, 1888, 99922233, 4.443322};
char buf[256] = {};
// 把資料轉換成字串
sprintf(buf, "%hhd %hd %d %lf", d.ch, d.sh, d.i, d.d);
FILE* fwp = fopen("data.bin", "wb");
if (NULL == fwp) {
perror("fopen");
return -1;
}
int ret = fwrite(buf, sizeof(buf[0]), strlen(buf), fwp);
printf("ret=%d\n", ret);
fclose(fwp);
fwp = NULL;
}
#include <stdio.h>
#include <string.h>
typedef struct Data {
char ch;
short sh;
int i;
double d;
} Data;
int main() {
FILE* frp = fopen("data.bin","rb");
if (NULL == frp) {
perror("fopen");
return -1;
}
Data d = {};
char buf[256] = {};
int ret = fread(buf, sizeof(buf[0]), sizeof(buf), frp);
printf("ret=%d\n", ret);
sscanf(buf, "%hhd %hd %d %lf", &d.ch, &d.sh, &d.i, &d.d);
printf("%hhd %hd %d %lf\n", d.ch, d.sh, d.i, d.d);
fclose(frp);
frp = NULL;
}
檔案位置指標:
檔案位置指標它指向檔案中即將要讀取的資料,以"r"、"r+"方式開啟檔案,檔案位置指標指向檔案的開頭,以"a"、"a+"方式開啟檔案,檔案位置指標指向檔案的末尾。(但是讀取可以在任意位置進行)
讀取資料時會從 檔案位置指標指向 的地方開始讀取,寫入資料時也會寫入到檔案位置指標所指向的地址,並且它會隨著讀寫操作自動移動。
注意:fprintf/fwrite寫入資料後立即讀取,之所以會失敗,是因為檔案位置指標指向著檔案的末尾。
void rewind(FILE *stream);
功能:把檔案的位置指標調整到檔案的開頭。
long ftell(FILE *stream);
功能:返回檔案位置指標指向了檔案中的第幾個位元組
int fseek(FILE *stream, long offset, int whence);
功能:設定檔案的位置指標
stream:要設定的檔案
offset:偏移值(正負整數)
whence:基礎位置
SEEK_SET 檔案開頭
SEEK_CUR 當前位置
SEEK_END 檔案末尾
whence+offset就是檔案指標最終設定的位置。
返回值:成功返回0,失敗返回-1。
檔案操作時的侷限性:
檔案的內容是連續儲存在磁碟上的,所以就導致需要進行以下操作:
向檔案中插入資料:
1、檔案位置指標調整到要插入的位置。
2、把後續的資料整體向後複製n(要插入的資料位元組數)個位元組。
3、檔案位置指標調整到要插入的位置,寫入資料。
從檔案中刪除資料:
1、檔案位置指標調整到要刪除的資料末尾。
2、把後續的資料整體向前複製nn(要刪除的資料位元組數)個位元組。
3、修改檔案的大小。
總結:所以,在程式執行時,建議在一開始就把檔案中的資料全部載入到記憶體中,程式在執行期間只針對這個資料記憶體進行增、刪、改、查等操作,在程式結束之前,再把資料從記憶體寫入到檔案中
檔案管理:
int remove(const char *pathname);
功能:刪除檔案
int rename(const char *oldpath, const char *newpath);
功能:重新命名檔案
int truncate(const char *path, off_t length);
功能:把檔案的內容設定為length位元組數
char *tmpnam(char *name);
功能:生成一個與當前檔案系統不重名的檔名。
int access(const char *pathname, int mode);
功能:檢查檔案的許可權
mode:
R_OK 讀許可權
W_OK 寫許可權
X_OK 執行許可權
F_OK 檔案是否存在
返回值:
檢查的許可權如果存在則返回0,不存在則返回-1。
實現一個mv命令
gcc xxx.c -o MV
./MV file1 file2 效果要等同於 mv file1 file2
以讀開啟file1,讀取資料到記憶體
以寫開啟file2,把從file1讀到的資料從記憶體中寫入到file2中
直到file1讀完寫完結束
刪除file1
main 函式的引數:
為了獲取命令列執行可執行檔案時後面附加的引數資訊
argc: 代表命令列引數的個數 包括./可執行檔案 本身也算一個引數
argv:一個儲存若干個字串的陣列,按順序儲存的是所有的命令列引數
#include <stdio.h>
#include <string.h>
#define N 1000010
char str[N];
int main(int argc, const char* argv[]) {
// for (int i = 0; i < argc; ++i) {
// printf("%s\n", argv[i]);
// }
if (strcmp(argv[0], "./MV") == 0) {
// printf("True");
FILE* frp = fopen(argv[1], "rb");
FILE* fwp = fopen(argv[2], "wb");
fread(str, sizeof str[0], sizeof str, frp);
fwrite(str, sizeof str[0], sizeof str, fwp);
fclose(frp), fclose(fwp);
frp = NULL, fwp = NULL;
remove(argv[1]);
} else {
printf("-1");
}
}
2、給2048遊戲增加讀檔、存檔功能
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <getch.h>
#include <string.h>
#include <time.h>
#define N 4
int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, 1, -1};
int d[N][N];
bool ok = false;
void clear_stdin() {
stdin->_IO_read_ptr = stdin->_IO_read_end;
}
char get_op(const char start, const char end) {
clear_stdin();
printf("請輸入指定選項[%c,%c]\n", start, end);
while (true) {
char op = getch();
if (start <= op && op <= end) {
printf("%c\n", op);
return op;
}
}
}
void erfile() {
if (access("data2048.bin", F_OK) == 0) {
remove("data2048.bin");
}
}
void readbin(int* score, int* idx) {
FILE* frp = fopen("data2048.bin", "rb");
if (frp == NULL) {
printf("當前沒有存檔,請選擇重新開始遊戲");
exit(-1);
}
fread(d, sizeof(int), N * N, frp);
fread(score, sizeof score, 1, frp);
fread(idx, sizeof idx, 1, frp);
fclose(frp);
frp = NULL;
erfile();
}
void writebin(int* score, int* idx) {
FILE* fwp = fopen("data2048.bin", "wb");
fwrite(d, sizeof(int), N * N, fwp);
fwrite(score, sizeof score, 1, fwp);
fwrite(idx, sizeof idx, 1, fwp);
fclose(fwp);
fwp = NULL;
}
void print() {
system("clear");
for (int i = 0; i < 9; ++i) {
if (i % 2 == 0) {
puts("-----------------------------");
} else {
for (int j = 0; j < 4; ++j) if (d[i / 2][j] != 0) {
printf("|%6d", d[i / 2][j]);
} else {
printf("| ");
}
printf("|\n");
}
}
puts("輸入任意數字退出遊戲!!!");
}
bool check() {
int cnt = 0;
ok = false;
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
cnt += d[i][j] == 0;
for (int dir = 0; dir < 4; ++dir) {
int fx = i + dx[dir], fy = j + dy[dir];
if (fx >= 0 && fy >= 0 && fx < 4 && fy < 4 && d[fx][fy] == d[i][j] && d[i][j]) {
ok = true;
return true;
}
}
}
}
return cnt != 0;
}
void get() {
if (ok) {
return ;
}
int w = rand() % 2;
w = 1 << w + 1;
int x = rand() % 4, y = rand() % 4;
while (d[x][y] != 0) {
x = rand() % 4, y = rand() % 4;
}
d[x][y] = w;
}
int move() {
int dir = getch() - 183, score = 0;
if (dir + 183 >= '0' && dir + 183 <= '9') {
return -1;
}
int cpd[4][4], cp[4][4];
memcpy(cp, d, sizeof d);
if (dir == 0) {
for (int j = 0; j < 4; ++j) {
for (int k = 0; k < 4; ++k) {
for (int i = 1; i < 4; ++i) if (d[i - 1][j] == 0 && d[i][j]) {
d[i - 1][j] ^= d[i][j] ^= d[i - 1][j] ^= d[i][j];
}
}
}
memcpy(cpd, d, sizeof d);
for (int j = 0; j < 4; ++j) {
for (int i = 0; i < 4; ++i) {
int fx = dx[dir] + i, fy = dy[dir] + j;
if (fx >= 0 && fy >= 0 && fx < 4 && fy < 4 && cpd[fx][fy] == cpd[i][j] && cpd[fx][fy]) {
d[fx][fy] += d[i][j];
d[i][j] = 0;
score += d[fx][fy];
}
}
}
for (int j = 0; j < 4; ++j) {
for (int k = 0; k < 4; ++k) {
for (int i = 1; i < 4; ++i) if (d[i - 1][j] == 0 && d[i][j]) {
d[i - 1][j] ^= d[i][j] ^= d[i - 1][j] ^= d[i][j];
}
}
}
} else if (dir == 1) {
for (int j = 3; j >= 0; --j) {
for (int k = 0; k < 4; ++k) {
for (int i = 2; i >= 0; --i) if (d[i + 1][j] == 0 && d[i][j]) {
d[i + 1][j] ^= d[i][j] ^= d[i + 1][j] ^= d[i][j];
}
}
}
memcpy(cpd, d, sizeof d);
for (int j = 3; j >= 0; --j) {
for (int i = 3; i >= 0; --i) {
int fx = dx[dir] + i, fy = dy[dir] + j;
if (fx >= 0 && fy >= 0 && fx < 4 && fy < 4 && cpd[fx][fy] == cpd[i][j] && cpd[fx][fy]) {
d[fx][fy] += d[i][j];
d[i][j] = 0;
score += d[fx][fy];
}
}
}
for (int j = 3; j >= 0; --j) {
for (int k = 0; k < 4; ++k) {
for (int i = 2; i >= 0; --i) if (d[i + 1][j] == 0 && d[i][j]) {
d[i + 1][j] ^= d[i][j] ^= d[i + 1][j] ^= d[i][j];
}
}
}
} else if (dir == 2) {
for (int i = 3; i >= 0; --i) {
for (int k = 0; k < 4; ++k) {
for (int j = 2; j >= 0; --j) if (d[i][j + 1] == 0 && d[i][j]) {
d[i][j + 1] ^= d[i][j] ^= d[i][j + 1] ^= d[i][j];
}
}
}
memcpy(cpd, d, sizeof d);
for (int i = 3; i >= 0; --i) {
for (int j = 3; j >= 0; --j) {
int fx = dx[dir] + i, fy = dy[dir] + j;
if (fx >= 0 && fy >= 0 && fx < 4 && fy < 4 && cpd[fx][fy] == cpd[i][j] && cpd[fx][fy]) {
d[fx][fy] += d[i][j];
d[i][j] = 0;
score += d[fx][fy];
}
}
}
for (int i = 3; i >= 0; --i) {
for (int k = 0; k < 4; ++k) {
for (int j = 2; j >= 0; --j) if (d[i][j + 1] == 0 && d[i][j]) {
d[i][j + 1] ^= d[i][j] ^= d[i][j + 1] ^= d[i][j];
}
}
}
} else if (dir == 3) {
for (int i = 0; i < 4; ++i) {
for (int k = 0; k < 4; ++k) {
for (int j = 1; j < 4; ++j) if (d[i][j - 1] == 0 && d[i][j]) {
d[i][j - 1] ^= d[i][j] ^= d[i][j - 1] ^= d[i][j];
}
}
}
memcpy(cpd, d, sizeof d);
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
int fx = dx[dir] + i, fy = dy[dir] + j;
if (fx >= 0 && fy >= 0 && fx < 4 && fy < 4 && cpd[fx][fy] == cpd[i][j] && cpd[fx][fy]) {
d[fx][fy] += d[i][j];
d[i][j] = 0;
score += d[fx][fy];
}
}
}
for (int i = 0; i < 4; ++i) {
for (int k = 0; k < 4; ++k) {
for (int j = 1; j < 4; ++j) if (d[i][j - 1] == 0 && d[i][j]) {
d[i][j - 1] ^= d[i][j] ^= d[i][j - 1] ^= d[i][j];
}
}
}
}
ok = true;
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) if (cp[i][j] != d[i][j]) {
ok = false;
}
}
return score;
}
char menu() {
system("clear");
puts("==============2048===============");
puts("1.新的遊戲 2.繼續上次 ");
puts("3.退出系統 ");
puts("=================================");
return get_op('1', '3');
}
char menu_save() {
system("clear");
puts("==========是否繼續遊戲===========");
puts("1.儲存並退出遊戲 2.不儲存退出遊戲");
puts("3.繼續遊戲 ");
puts("=================================");
return get_op('1', '3');
}
void work(int score, int idx, bool isnew) {
if (isnew) get();
print();
while (check()) {
idx += 1;
int suc = move();
if (suc != -1) {
score += suc;
} else {
int op = menu_save() - '0';
if (op == 1) {
writebin(&score, &idx);
return ;
} else if (op == 2) {
break;
}
}
get(), print();
}
erfile();
printf("遊戲已經結束,您的得分是%d, 共用了%d步\n", score, idx);
}
int main() {
srand(time(0));
int op = menu() - '0';
int score = 0, idx = 0;
if (op == 1) {
work(score, idx, true);
} else if (op == 2) {
readbin(&score, &idx);
work(score, idx, false);
}
}