文章目录
构造类型
枚举类型
如果定义不相干的常量,使用宏定义;如果需要定义一组相关的常量,使用枚举。以后正式开发,switch的case后访问的就是枚举
- 定义:
我们一般情况下,定义常量使用宏定义(#define宏名称值),宏定义非常适合没有关联关系的常量;但是有时候我们可能需要对一组拥有关联关系的量进行定义,比如周一——周日,1月~12月、方向(上下左右中)等
,那么使用宏定义就不是很清晰,这个时候就需要使用到枚举
枚举的存在就是将多个拥有关系的常量组合在一起,提高代码的可读性
- 说明:
枚举类型定义了一组常量,我们在开发中直接使用这些常量(常用)
当然枚举类型也可以类似于结构体一样定义变量等操作(不常用)
枚举常量是有默认值,从0开始依次+1;我们可以在定义时指定它的默认值,如果个别没有赋值,可以根据赋值依次+1推导
- 特点:
定义了一组常量,类似于定义了多个自定义常量(宏定义)
提供了代码的可读性
- 定义语法:
定义枚举类型名以后就可以定义该枚举类型的变量
enum 枚举类型名 变量列表;
在定义枚举类型的同时定义该枚举类型的变量
enum 枚举类型名{枚举元素列表} 变量列表;
直接定义枚举变量
enum {枚举元素列表} 变量列表;
- 案例:
#include <stdio.h>
void test2()
{
//定义枚举
enum CaiQuan
{
SHI_TOU,JIAN_DAO,BU
};
printf("请输入0~2之间的整数:\n[0-石头,1-剪刀,2-布]\n");
int number;
scanf("%d",&number);
switch(number)
{
case SHI_TOU:
printf("石头\n");
break;
case JIAN_DAO:
printf("剪刀\n");
break;
case BU:
printf("布\n");
break;
}
}
int main(int argc,char *argv[])
{
test2();
return 0;
}
typedef
- 说明:给类型重命名,不会影响到类型本身
- 作用:给已有的类型起别名
- 格式:
typedef 已有的类型名 新别名;
- 使用:
//方式1:先定义结构体的类型,再重命名
//定义一个结构体
struct Student
{
int id;
char *name;
char sex;
int age;
};
//类型重命名
typedef struct Student Stu;
//定义结构体变量
struct Stu stu = {1,"张三",'w',21};
//方式2:定义结构体的同时重命名
typedef struct PersonInfo
{
int a;
double b;
} Per;//结构体类型的别名,本质上还是数据类型
//定义变量
struct Per per = {2,5};
应用场景:
数据类型复杂(结构体、共用体、枚举、结构体指针、无符号的长整型)时使用
为了跨平台兼容性,例如:
1.size_t:类型重命名后的数据类型:
typedef unsigned long size_t;
2.unit_16:类型重命名后数据类型
案例:
#include <stdio.h>
int main(int argc,char *argv[])
{
long p;
FILE *fp;
if((fp = fopen(argv[1],"a") == NULL)) //此时的mode:a代表追加(a是append的缩写)
{
printf("文件打开失败!");
return -1;
}
//获取当前位置
p = ftell(fp);
printf("p = %ld\n",p);
//向文件添加数据
fputs("data",fp);
p = ftell(fp);
printf("p = %ld\n",p);
fclose(fp);
return 0;
}
文件操作(文件IO)
概述
- 什么是文件
文件是保存在外存储器(一般代指磁盘,U盘,移动硬盘等)的数据的集合
- 文件操作体现在哪几个方面
1.文件内容的读取
2.文件内容的写入
数据的读取和写入可被视为针对文件进行输入(Input)和输出(Output)操作,此时数据像水流一样从外存储器流向内存,或者从内存流向外存储器,所以系统形象的 称文件操作为文件流
C语言程序对文件的操作采用“文件缓冲机制”。就是说在程序对文件的数据读写并不是直接操作文件中的数据,而是系统会为文件在内存中创建“文件缓冲区”,程序对文件的操作,其实是在缓冲区进行的
- 文件的分类
根据数据的存储方式划分:
①文本文件(ascii文件)
②二进制文件
文件的标识
①文件系统中:路径+文件名,举例:
d:/aaa/bbb.txt
②C语言程序中:文件指针(文件类型指针),语法:FILE* 指针变量名;
- 文件操作的步骤:
①打开文件
②文件处理(读写文件)
③关闭文件
文件的操作
文件的打开与关闭
打开文件
打开文件,让系统为文件创建文件缓冲区
- 函数名:fopen()
- 头文件:
#include <stdio.h>
- 函数原型:
FIFE* fopen(const char *path,const char *mode);
- 函数功能:打开文件,并为文件创建缓冲区
- 函数参数:
- path:目标文件的路径
- mode:文件打开的方式(读-r,写-w,读写-r+,二进制读-rb,追加-a)
- 返回值:
- 成功:返回文件指针FILE*(缓冲区首地址)
- 失败:返回NULL
关闭文件
文件关闭,文件使用完毕一定要记得释放内存
函数名:fcolse
头文件:
#include <stdio.h>
函数原型:
int fclose(FILE* fp);
函数功能
- fp:已经打开的文件指针
返回值:
- 成功:返回0
- 失败:返回EOF(-1)
文件打开与关闭案例
#include <stdio.h>
int main(int argc,char *argv[])
{
//在命令行执行./a.out的时候,传递一个需要打开的目录文件的地址
if(argc < 2)
{
printf("输入有误,请按照<%s 文件路径>格式输入\n",argv[0]);// ./a/out ./demo01.c
return -1;
}
//根据提供的文件路径,打开文件(mode:r,w,rw)
FILE* fp = fopen(argv[1],"r");
//校验文件是否读取成功
if(!fp)
{
perror("文件打开失败!\n");
return -1;
}
puts("文件打开成功!");
//关闭打开的文件
int ret = fclose(fp);
if(ret == -1)
{
perror("文件关闭失败!");
return -1;
}
puts("文件关闭成功!");
return 0;
}
文件的顺序读写
单字符读取
函数名:fgetc
头文件:
#include <stdio.h>
函数原型:
int fgetc(FILE* fp);
函数功能:从fp代表的文件中获取一个字符
函数参数:
- fp:我们需要操作的文件的指针
返回值:
- 成功:返回读取到的字符
- 失败:或者文件末尾,返回EOF(-1)
方式1:
#include <stdio.h>
int main(int argc,char *argv[])
{
//在命令行执行./a.out的时候,传递一个需要打开的目录文件的地址
if(argc < 2)
{
printf("输入有误,请按照<%s 文件路径>格式输入\n",argv[0]);// ./a/out ./demo01.c
return -1;
}
//根据提供的文件路径,打开文件(mode:r,w,rw)
FILE* fp = fopen(argv[1],"r");
//校验文件是否读取成功
if(!fp)
{
perror("文件打开失败!\n");
return -1;
}
puts("文件打开成功!");
//读取文件
int ret = 0;
while((ret = fgetc(fp)) != -1)
{
printf("%c",ret);
}
//关闭打开的文件
int cet = fclose(fp);
if(cet == -1)
{
perror("文件关闭失败!");
return -1;
}
puts("文件关闭成功!");
return 0;
}
方式2:
#include <stdio.h>
int main(int argc,char *argv[])
{
// 在命令行执行./a.out的时候,传递一个需要打开的目录文件的地址
if(argc < 2)
{
printf("输入有误,请按照<%s 文件路径>格式输入\n",argv[0]);// ./a.out ./demo01.c
return -1;
}
// 根据提供的文件路径,打开文件(mode:r,w,rw)
FILE* fp = fopen(argv[1],"r");
// 校验文件是否读取成功
if(!fp)
{
perror("文件打开失败!\n");
return -1;
}
puts("文件打开成功!");
// 单字符读取文件
char ch;
// 循环读取文件中所有字符
while((ch = fgetc(fp))!= EOF)
printf("%c",ch);
// 关闭打开的文件
int ret = fclose(fp);
if(ret == -1)
{
perror("文件关闭失败!");
return -1;
}
puts("文件关闭成功!");
return 0;
多字符读取
函数名:fgets();
头文件:
#include <stdio.h>
函数原型:
char* fgets(char *buf,int size,FILE* fp);
函数功能:从fp代表的文件中获取size个字符(size大小以字节为单位),放置在buf代表的内存中
函数参数:
- buf:内存空间首地址,用于存放读取的字节
- size:待读取的字符,实际读取size
- fp:已经打开文件的指针
返回值:
- 成功:返回buf(返回得到的字符串)
- 失败:或者文件末尾,返回NULL
案例:
#include <stdio.h>
#include <string.h>
int main(int argc,char *argv[])
{
if(argc < 2)
{
printf("输入有误,请按照<%s 文件路径>格式输入\n",argv[0]);
}
FILE* fp;
char* arr;
fp = fopen(argv[1],"r");
if(!fp)
{
perror("文件打开失败!\n");
return -1;
}
puts("文件打开成功");
char buf[64] = {0};
fgets(buf,64,fp);//多字符串的读取
while(fgets(buf,64,fp) != NULL)
{
printf("%s",buf);
memset(buf,0,sizeof(buf));
}
printf("\n");
int ret = fclose(fp);
if(ret == -1)
{
perror("文件关闭失败!\n");
return -1;
}
puts("文件关闭成功!\n");
return 0;
}
单字符写入
- 函数名:fputc()
- 头文件:
#include <stdio.h>
- 函数原型:
int fputc(int c,FILE* fp);
- 函数功能:向fp代表的文件中写入一个字符c
- 函数的参数
- c:待写入的字符
- fp:已打开的文件指针
- 返回值:
- 成功:返回字符c
- 失败:返回EOF(-1)
- 案例:
#include <stdio.h>
int main(int argc, char *argv[])
{
// argc:表示命令行输入的文件个数
if (argc < 3)
{
printf("输入有误,请按照<%s 文件路径 文件数据>格式输入\n", argv[0]);
return -1;
}
// argv[1]:表示需要读取内容的文件,argv[2]表示需要写入字符的文件
FILE *fp;
fp = fopen(argv[1], "w");
if (!fp)
{
perror("文件打开失败!\n");
return -1;
}
puts("文件打开成功\n");
while (*argv[2] != '\0')
{
fputc(*argv[2], fp);
argv[2]++; // 指针偏移
}
// 关闭文件
int ret = fclose(fp);
if (ret == -1)
{
perror("文件关闭失败!\n");
return -1;
}
puts("文件关闭成功\n");
return 0;
}
多字符写入
函数名:fputs()
头文件:
#include <stdio.h>
函数原型:
int fputs(const char* buf,FILE* fp);
函数功能:向fp代表的文件中写入一个字符数组buf
函数的参数
- buf:待写入的字符数组(写入缓冲区)
- fp:已打开的文件指针
返回值:
- 成功:返回非负整数比0大(>0)
- 失败:返回EOF(-1)
案例:
#include <stdio.h>
int main(int argc,char *argv[])
{
if(argc < 3)
{
printf("输入有误,请按照<%s 文件路径 文本数据>格式输入\n",argv[0]);
return -1;
}
FILE* fp = fopen(argv[1],"w");
if(!fp)
{
perror("文件打开失败!");
return -1;
}
fputs(argv[2],fp);
fclose(fp);
return 0;
}
综合案例:文件拷贝
实现从a文件读取内容,写入到b文件
#include <stdio.h>
#include <string.h>
int main(int argc,char *argv[])
{
if(argc < 3)
{
printf("请以正确的方式%s",argv[0]);
return -1;
}
FILE* fp1;
FILE* fp2;
fp1 = fopen(argv[1],"r");
fp2 = fopen(argv[2],"w");
if(!fp1 || !fp2)
{
perror("文件打开失败!");
return -1;
}
puts("文件打开成功!\n");
//读取第一个文件中的内容,使用fgets
//创建一个缓冲区(也就是每次获取字节的大小)
char buf[64];
fgets(buf,64,fp1);
while(fgets(buf,64,fp1) != NULL)
{
fputs(buf,fp2);
memset(buf,0,64);
}
//判断文件是否读取完成
int k = feof(fp1);
if(!k)
{
printf("文件读取未完成\n");
}
else
{
printf("文件读取完成\n");
}
//关闭文件
int r1 = fclose(fp1);
int r2 = fclose(fp2);
if(r1 == -1 || r2 == -1)
{
perror("文件关闭失败!");
return -1;
}
puts("文件关闭成功\n");
return 0;
}
判别文件结束
函数名:feof(fp)
头文件:
#include <stdio.h>
函数原型:
int feof(FILE* fp);
函数功能:在读fp指向的文件时判断是否遇到文件结束
函数参数:
- fp:已打开文件的指针
返回值:
- 文件未读取完毕:返回0
- 文件已读取完毕:返回非0
数据块的读写(二进制)
数据块的读取
- 函数名:fread
- 函数原型:
size_t fread(void* ptr,size_t size,size_t count,FILE* fp);
- 函数功能:从fp代表的文件中以字节为单位(一个数据块)读取count个数据块存放在内存中
- 函数参数:
- ptr:内存空间首地址,用于存放读取到的数据(任意类型缓冲数据)
- size:数据块大小,以字节为单位
- count:待读取的数据块的个数
- fp:已打开的文件指针
- 返回值:
- 成功:返回实际读取的字节数大小
- 失败:返回<0
#include <stdio.h>
#define SIZE 4 // 存放学生个数
struct Student
{
char name[20];
int num;
int age;
char addr[50];
} stus[SIZE];
int main(int argc, char *argv[])
{
int i;
FILE *fp;
stus[1].num = 6;
stus[1].age = 13;
fp = fopen("stu-list","rb");
if (fopen("stu-list", "rb") == NULL)
{
perror("文件打开失败!");
return -1;
}
// 循环读取二进制读取
for (i = 0; i < SIZE; i++)
{
fread(&stus[i], sizeof(struct Student), 1, fp);
printf("%-10s%-4d%-4d%-20s\n", stus[i].name, stus[i].num, stus[i].age, stus[i].addr);
}
// 关闭文件
fclose(fp);
return 0;
}
数据块的写入
函数名:fwrite
函数原型:
size_t fwrite(const void* ptr,size_t size,size_t count,FILE* fp);
函数功能:向fp代表的文件中以size为一个数据块写入count个数据块到fp
函数参数:
ptr:内存空间首地址,用于存放写入的数据(任意类型缓冲数据)
size:数据块大小,以字节为单位
count:待写入的数据块的个数
fp:已打开的文件指针
返回值:
- 成功:返回实际写入的字节数
- 失败:写入完毕,返回<0
#include <stdio.h>
#define SIZE 4 // 存放学生个数
struct Student
{
char name[20];
int num;
int age;
char addr[50];
} stus[SIZE];
int main(int argc, char *argv[])
{
int i;
FILE *fp;
stus[1].num = 6;
stus[1].age = 13;
stus[2].num = 3;
stus[2].age = 56;
stus[3].num = 10;
stus[3].age = 23;
fp = fopen("stu-list","wb");
if (fopen("stu-list", "wb") == NULL)
{
perror("文件打开失败!");
return -1;
}
// 循环读取二进制读取
for (i = 0; i < SIZE; i++)
{
fwrite(&stus[i], sizeof(struct Student), 1, fp);
//printf("%-10s%-4d%-4d%-20s\n", stus[i].name, stus[i].num, stus[i].age, stus[i].addr);
}
// 关闭文件
fclose(fp);
return 0;
}
文件的随机读写
- 说明:C语言允许程序员在读写文件内容的时候,可在指定位置上读写数据
- 文件随机读写的核心操作:文件位置指针的定位
- 文件位置指针移动方法:
- rewind
- 头文件:
#include <stdio.h>
- 函数原型:
void rewind(FILE* fp);
- 函数功能:将文件位置指针定位到文件开头
- 函数参数:
- fp:已经打开文件的指针
- 返回值:空类型
- 头文件:
- fseek
- 头文件:
#include <stdio.h>
- 函数原型:
int fseek(FILE* fp,long offset,int whence);
- 函数功能:将文件位置指针定位到指定位置
- 函数参数:
- fp:已经打开的文件的指针
- offset:相对于参考位置的偏移量
- whence:参考位置
- SEEK_SET或0:表示文件头
- SEEK_CUR或1:表示当前读写的位置
- SEEK_END或2:表示文件尾
- 返回值:
- 成功:0
- 失败:-1
- 头文件:
- ftell
- 头文件:
#include <stdio.h>
- 函数原型:
long ftell(FILE* fp);
- 函数功能:获取文件位置指针当前位置
- 函数参数:
- fp:已经打开的文件指针
- 返回值:
- 成功:文件位置指针的当前位置
- 失败:-1
- 头文件:
- rewind
案例:
#include <stdio.h>
int main(int argc,char *argv[])
{
//创建两个指针,用来接收打开的文件
FILE* fp1,*fp2;
if(argc < 2)
{
perror("请使用<%s 被读取文件,被写入文件>的方式!\n");
return -1;
}
//打开文件
fp1 = fopen(argv[1],"r");
fp2 = fopen("argv[2]","w");
//第一次,读取文件内容通过控制台打印
while(!feof(fp1))
{
putchar(getc(fp1));//输出到控制台
}
//将指针指向文件的头,否则下次读取文件,会读取不到内容
rewind(fp1);
//第二次,读取文件内容将其拷贝至fp2对应的文件中
while(!feof(fp1))
{
putc(getc(fp1),fp2);//输出到文件
}
//关闭文件
fclose(fp1);
fclose(fp2);
return 0;
}
案例2
#include <stdio.h>
int main(int argc,char *argv[])
{
// 创建两个指针,用来接收打开的文件
FILE *fp1, *fp2;
if(argc < 2)
{
perror("请使用<%s 被读取文件 被写入文件>的方式!\n");
return -1;
}
// 打开文件
fp1 = fopen(argv[1],"r");
fp2 = fopen(argv[2],"w");
// 第一次,读取文件内容通过控制台打印
while(!feof(fp1))
putchar(getc(fp1));// 输出到控制台
// 将指针指向文件的头,否则下次读取文件,会读取不到内容
rewind(fp1);
// 第二次,读取文件内容将其拷贝至fp2对应的文件中
while(!feof(fp1))
putc(getc(fp1),fp2);// 输出到文件
// 关闭文件
fclose(fp1);
fclose(fp2);
return 0;
}
案例3
#include <stdio.h>
int main(int argc,char *argv[])
{
long p;
FILE* fp;
fp = fopen(argv[1],"a");
if(fopen(argv[1],"a") == NULL)//a表示追加
{
perror("文件打开失败!");
return -1;
}
//
p = ftell(fp);
printf("p = %ld\n",p);
//向文件添加数据
fputs("data",fp);
printf("p = %ld\n",p);
fclose(fp);
return 0;
}