文件
文本文件 : 存储时,是将字符的ascii值存在磁盘中,取的时候将数值(ascii)翻译成对应的字符
二进制文件(例如图片): 存的是二进制,取的是二进制
文件指针
当打开一个文件时,系统会返回一个结构体,这个结构体有对此文件操作的所有信息
调用fopen时,系统返回这个结构体地址
打开文件
FILE *fp = fopen( "pathname", 打开的方式 );
注意: 打开的选项
只有带r的选项,如果文件不存在,则不创建文件
带w选项的,打开时会清空文件
fopen的返回值: 如果成功返回FILE结构体地址,失败返回NULL
返回的文件流指针标识了打开的那个文件
//r 只读 ,如果文件不存在,不创建
//w 只写 清空文件 ,如果文件不存在,创建文件
//r+ 可读可写 如果文件不存在,不创建
//w+ 可读可写清空文件, 如果文件不存在,创建文件
//a追加 如果文件不存在,会创建文件
//b 二进制文件
//打开一个文件,成功返回FILE结构体地址,失败返回NULL
FILE *fp = fopen("./a.txt","w");
if (NULL == fp)
{
perror("open");
return;
}
fputc
功能:写入一个字符到文件中
#include <stdio.h>
int fputc(int ch, FILE * stream);
/*
功能:将ch转换为unsigned char后写入stream指定的文件中
参数:
ch:需要写入文件的字符
stream:文件指针
返回值:
成功:成功写入文件的字符
失败:返回-1
*/
FILE *fp = fopen("./a.txt","w");
if (NULL == fp)
{
perror("open");
return;
}
char buf[] = "hello";
int i = 0;
while (buf[i] != 0)
{
fputc(buf[i],fp);
i++;
}
fgetc
#include <stdio.h>
int fgetc(FILE * stream);
/*
功能:从stream指定的文件中读取一个字符
参数:
stream:文件指针
返回值:
成功:返回读取到的字符
失败:-1
*/
feof
如果读取文件不是纯文本,有像-1这种数值,那么久不可以使用EOF(-1)作为文件的结尾。
#include <stdio.h>
int feof(FILE * stream);
/*
功能:检测是否读取到了文件结尾。判断的是最后一次“读操作的内容”,不是当前位置内容(上一个内容)。
参数:
stream:文件指针
返回值:
非0值:已经到文件结尾
0:没有到文件结尾
*/
fgets 从文件读取字符串
注意: fgets读取遇到\n结束
#include <stdio.h>
char * fgets(char * str, int size, FILE * stream);
/*
功能:从stream指定的文件内读入字符,保存到str所指定的内存空间,直到出现换行字符、读到文件结尾或是已读了size - 1个字符为止,最后会自动加上字符 '\0' 作为字符串结束。
参数:
str:字符串
size:指定最大读取字符串的长度(size - 1)
stream:文件指针
返回值:
成功:成功读取的字符串
读到文件尾或出错: NULL
*/
fputs 向文件写入字符串
#include <stdio.h>
int fputs(const char * str, FILE * stream);
/*
功能:将str所指定的字符串写入到stream指定的文件中,字符串结束符 '\0' 不写入文件。
参数:
str:字符串
stream:文件指针
返回值:
成功:0
失败:-1
*/
文件拷贝案例
// 打开src文件
FILE *src = fopen("./a.txt", "r");
if (!src) {
perror("src open fail");
return -1;
}
FILE *dest = fopen("./my_a.txt", "w");
if (!dest) {
perror("dest open fail");
return -1;
}
char ch = 0;
while (1)
{
// 读取src一个字符
ch = fgetc(src);
// 如果读到了文件末尾,就结束了
if (feof(src))
break;
// 写入dest文件中
fputc(ch,dest);
// 打印到终端
ch = fgetc(src);
if (feof(src))
break;
// stdout是输出到显示器,程序运行时系统自动打开
fputc(ch, stdout);
}
// 关闭文件
fclose(src);
fclose(dest);
fprintf
#include <stdio.h>
/*
int fprintf(FILE * stream, const char * format, ...);
功能:根据参数format字符串来转换并格式化数据,然后将结果输出到stream指定的文件中,指定出现字符串结束符 '\0' 为止。
参数:
stream:已经打开的文件
format:字符串格式,用法和printf()一样
返回值:
成功:实际写入文件的字符个数
失败:-1
*/
int year = 2018;
int month = 10;
int day = 27;
char buf[1024] = "";
FILE *fp = NULL;
fp = fopen("./fprintf.txt", "w");
if (!fp)
{
perror("");
return -1;
}
//sprintf(buf,"%04d:%02d:%02d\n",year,month,day);
//fputs(buf,fp);
fprintf(fp, "%04d:%02d:%02d\n", year, month, day);
fclose(fp);
system("pause");
return 0;
fscanf
#include <stdio.h>
/*
int fscanf(FILE * stream, const char * format, ...);
功能:从stream指定的文件读取字符串,并根据参数format字符串来转换并格式化数据。
参数:
stream:已经打开的文件
format:字符串格式,用法和scanf()一样
返回值:
成功:参数数目,成功转换的值的个数
失败: - 1
*/
int year = 0,month=0,day=0;
FILE *fp = NULL;
fp = fopen("./fprintf.txt", "r");
if (!fp)
{
perror("");
return -1;
}
fscanf(fp,"%d:%d:%d\n",&year,&month,&day);
printf("%d %d %d \n",year,month,day);
system("pause");
return 0;
fwrite
#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
*/
STD num[3] = { {1,"lucy"},{2,"bob"},{3,"peter"} };
FILE *fp = NULL;
int cont = 0;
fp = fopen("fwrite.txt","w");
if (!fp)
{
perror("");
return -1;
}
//fwrite 第二个参数写1 ,是为了返回值刚好是写入文件的字节数
cont = fwrite(num, 1,sizeof(num),fp);
//cont = fwrite(num, sizeof(num), 1,fp);
printf("cont=%d\n",cont);
system("pause");
fread
#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
*/
STD num[3];
memset(num,0,sizeof(num));
FILE *fp = NULL;
int cont = 0;
fp = fopen("fwrite.txt", "r");
if (!fp)
{
perror("");
return -1;
}
fread(num,1,sizeof(num),fp);
for (int i = 0; i < 3; i++)
{
//cont = fread( &num[i] ,1,sizeof(STD),fp );
//printf("cont =%d\n",cont);
printf("%d %s\n",num[i].id,num[i].name);
}
fclose(fp);
fseek
#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
*/
FILE *fp = NULL;
fp = fopen("fseek.txt", "w");
if (!fp)
{
perror("");
return -1;
}
fputs("helloworldheihei",fp);
fseek(fp,0,SEEK_SET);//移动光标到头
fputs("seek", fp);
fseek(fp, -5, SEEK_END);//移动光标到头
fputs("abc", fp);
fclose(fp);
rewind
: 作用: 将光标移动到开头,和fseek(fp,0,SEEK_SET);
ftell
#include <stdio.h>
long ftell(FILE *stream);
/*
功能:获取文件流(文件光标)的读写位置。
参数:
stream:已经打开的文件指针
返回值:
成功:当前文件流(文件光标)的读写位置
失败:-1
*/
stat
#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; //最后一次改变时间(指属性)
};
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
int main()
{
struct stat buf;//buf结构体一定要有空间
int ret = 0;
ret = stat("fwrite.txt",&buf);
if (ret < 0)//如果返回值小于0,代表文件不存在
{
printf("file notfound\n");
}
printf("%d\n",buf.st_size);//buf.st_size 这个是文件大小
system("pause");
return 0;
}
linux和windwos \n的区别
删除文件,重命名
#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
文件缓冲区
普通文件刷新缓冲区的方法,3种
- 缓冲区满
- fflush函数强制刷新
- 程序正常退出
windows下标准输出stdout文件,没有缓冲区的
linux 有
标准输入不能调用fflush强制刷新
案例:翻译软件
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#define FILENAME "dict.txt"
typedef struct _dict
{
char *key;
char *content;
}DICT;
// 初始化字典
// 因为要初始化一个指针变量,所以要拿到指针的地址,所以用二级指针来操作
void dict_INIT(DICT **temp)
{
DICT *p = (DICT *)malloc(sizeof(DICT)*2);
// 使用之前先申请空间,因为DICT里面都是字符指针变量
p[0].key = (char *)malloc(sizeof("hello")+1);
p[0].content = (char *)malloc(sizeof("你好")+1);
p[1].key = (char *)malloc(sizeof("world")+1);
p[1].content = (char *)malloc(sizeof("世界")+1);
strcpy(p[0].key, "hello");
strcpy(p[0].content, "你好");
strcpy(p[1].key, "world");
strcpy(p[1].content, "世界");
*temp = p;
}
// 查找字典
int search_dict(char *cmd, DICT *dict, int len, char *content)
{
int i;
for(i = 0; i < len; i++)
{
if(strcmp(cmd, dict[i].key) == 0)
{
strcpy(content, dict[i].content);
return 1;
}
}
return 0;
}
int main(int argc, char const *argv[])
{
DICT *dict = NULL;
dict_INIT(&dict);
char cmd[256] = "";
char content[256] = "";
while (1)
{
printf("请输入需要翻译的单词:");
fgets(cmd, sizeof(cmd), stdin);
cmd[strlen(cmd) - 1] = '\0';
if (search_dict(cmd, dict, 2, content))
{
printf("%s 翻译为:%s\n", cmd, content);
}
else
{
printf("not translate\n");
}
}
return 0;
}
贪吃蛇案例
#include <string.h>
#include <stdlib.h>
#include <windows.h>
#include <conio.h>
#include <unistd.h>
#include <sys/time.h>
#include <stdio.h>
#define WIDE 60
#define HIGH 16
// 1. 设置地图范围(边界) 宽 高
// 2. 初始化蛇, 初始化食物
// 3. 将蛇和食物显示在屏幕上
// 4. 蛇的移动(通过wasd按键控制蛇的移动方向
/*
1. 蛇碰到墙
2. 蛇碰到蛇的身体
3. 蛇碰到障碍物
4. 蛇碰到食物(蛇的身体增长一节,原食物消失,生成新的食物,增加分数)
*/
// 5. 蛇的死亡、积分
typedef struct snake_body
{
int x, y;
} BODY;
// 把蛇相关的都写到一个结构体里
// 把变量都放到这里,结构体申请在堆区,不用来回地传参
typedef struct snake
{
// 身体, 身体上的每一节坐标都是BODY类型
BODY list[WIDE * WIDE];
// 蛇的身体大小
int length;
// 食物的坐标
BODY food;
// 用来更新坐标
COORD coord;
// 蛇x和y移动的方向
int dx, dy;
// 得分
int score;
BODY tail;
} SNAKE;
void init_ui()
{
for (int i = 0; i < HIGH; i++)
{
for (int j = 0; j < WIDE; j++)
{
printf("$");
}
printf("\n");
}
}
// 初始化食物
void init_food(SNAKE *snake)
{
// s设置随机数种子
srand(time(NULL));
// 初始化食物坐标
snake->food.x = rand() % WIDE;
snake->food.y = rand() % HIGH;
}
// 初始化蛇
void init_snake(SNAKE *snake)
{
// 初始化蛇头坐标
snake->list[0].x = WIDE / 2;
snake->list[0].y = HIGH / 2;
// 初始化蛇尾坐标
snake->list[1].x = WIDE / 2 - 1;
snake->list[1].y = HIGH / 2;
init_food(snake);
snake->dx = 1;
snake->dy = 0;
// 初始化蛇的长度
snake->length = 2;
snake->score = 0;
}
void show_ui(SNAKE *snake)
{
// 显示蛇身体
for (int i = 0; i < snake->length; i++)
{
// 这里需要转一下类型
snake->coord.X = (short)snake->list[i].x;
snake->coord.Y = (short)snake->list[i].y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake->coord);
if (i == 0)
{
printf("@");
}
else
{
printf("$");
}
}
snake->coord.X = (short)snake->food.x;
snake->coord.Y = (short)snake->food.y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake->coord);
printf("$");
// 将原来尾巴的位置显示为空格
snake->coord.X = (short)snake->tail.x;
snake->coord.Y = (short)snake->tail.y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake->coord);
printf(" ");
}
void move_snake(SNAKE *snake)
{
// 记录尾巴的坐标
snake->tail = snake->list[snake->length - 1];
// 更新蛇的身体坐标,除去蛇头
// 从倒数第二节身体开始,后一节的身体的坐标等于前一节身体的坐标
for (int i = snake->length - 1; i > 0; i--)
{
snake->list[i] = snake->list[i - 1];
}
// 更新蛇头
snake->list[0].x += snake->dx;
snake->list[0].y += snake->dy;
}
void control_snake(SNAKE *snake)
{
char key = 0;
while (kbhit())
{
key = _getch();
}
switch (key)
{
case 'a':
snake->dx = -1;
snake->dy = 0;
break;
case 'd':
snake->dx = 1;
snake->dy = 0;
break;
case 'w':
snake->dx = 0;
snake->dy = -1;
break;
case 's':
snake->dx = 0;
snake->dy = 1;
break;
}
}
void game_end(SNAKE *snake)
{
snake->coord.X = 25;
snake->coord.Y = 25;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake->coord);
printf("game over, your score:%d\n", snake->score);
}
void snake_eat_self(SNAKE *snake)
{
// 如果蛇头和蛇的身体的任意一节身体坐标相同,则为吃到自己
for (int i = 1; i < snake->length; i++)
{
if (snake->list[0].x == snake->list[i].x &&
snake->list[0].y == snake->list[i].y)
{
game_end(snake);
exit(0);
}
}
}
void snake_eat_food(SNAKE *snake)
{
// 如果蛇头和食物坐标重叠
if (snake->list[0].x == snake->food.x &&
snake->list[0].y == snake->food.y)
{
// 分数增加
snake->score+=10;
// 身体增长一节
snake->length++;
// 生成新的食物的同时,原食物已经消失了
init_food(snake);
}
}
void start_game(SNAKE *snake)
{
// 蛇是否碰到墙
while (snake->list[0].x < WIDE && snake->list[0].x >= 0 &&
snake->list[0].y < HIGH && snake->list[0].y >= 0)
{
// 控制蛇的方向
control_snake(snake);
// 更新蛇的坐标
// 蛇移动
move_snake(snake);
// system("clear");
show_ui(snake);
// 蛇是否碰到自己
snake_eat_self(snake);
// 蛇是否碰到食物(碰到食物,原食物消失,产生新的食物
snake_eat_food(snake);
// 延时0.5秒
// 这里的大小写别写错了,如果用小写的sleep()无法实现延时
Sleep(200);
}
game_end(snake);
}
void init_wall()
{
for (int i = 0; i <= HIGH; i++)
{
for (int j = 0; j <= WIDE; j++)
{
if (j == 0 || j == WIDE || i == 0 || i == HIGH)
printf("@");
else
printf(" ");
}
printf("\n");
}
}
void hide_cur()
{
// 隐藏控制台光标
CONSOLE_CURSOR_INFO cci;
cci.dwSize = sizeof(CONSOLE_CURSOR_INFO);
cci.bVisible = FALSE;
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci);
}
int main(int argc, char const *argv[])
{
// init_ui();
SNAKE *snake = (SNAKE *)malloc(sizeof(SNAKE));
// 隐藏控制台光标
hide_cur();
// 初始化墙
init_wall();
// 初始化蛇和食物
init_snake(snake);
show_ui(snake);
start_game(snake);
return 0;
}