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);
}
}
标签:文件,int,fy,读写,C语言,char,fx,&&
From: https://www.cnblogs.com/sleeeeeping/p/18200196