并非从0开始的c++ day9
后续部分课程涉及数据结构,这里直接跳过
配置文件读写练习
需求
需求:从根目录config文件中读取数据,建立一个结构体含有key和value两个属性,可以通过config中响应的key访问对应的value,且可以跳过不需要的字符
判断文件有效行数
//获取文件有效行数
int getFileLine(const char* filePath)
{
FILE* file = fopen(filePath, "r");
if (file == NULL)
{
return -1;
}
char buf[1024] = { 0 };
int line = 0;
while (fgets(buf, 1024, file) != NULL)
{
//检测是否有效
if (isInvalidLine(buf))
{
line++;
}
}
return line;
}
根据字符串判断是否是有效行
//判断传入的字符串是否为有效行 是返回1 否0
int isInvalidLine(char* str)
{
if (str[0] == '\n' || strchr(str, ':') == NULL)
{
return 0;
}
return 1;
}
strchr可以用来查找字符串是否含有传入的字符
文件解析
//文件解析
void parseFile(const char* filePath, int lines, struct ConfigInfo** configInfo)
{
struct ConfigInfo* config = malloc(sizeof(struct ConfigInfo) * lines);
if (config == NULL)
{
return;
}
FILE* file = fopen(filePath, "r");
if (file == NULL)
{
return;
}
char buf[1024] = { 0 };//将读取的每行数据放入到buf中
int index = 0;
while (fgets(buf,1024,file)!= NULL)
{
if (isInvalidLine(buf))
{
//heroId : 1
//清空key和value值
memset(config[index].key, 0, 64);
memset(config[index].value, 0, 64);
char* pos = strchr(buf, ':');
//截取key数据
strncpy(config[index].key, buf, pos -buf);
//截取value数据
strncpy(config[index].value, pos+1,strlen(pos+1)-1);//-1是不用文件中的换行符
printf("key = %s\n value = %s \n", config[index].key, config[index].value);
index++;
}
memset(buf, 0, 1024);
}
*configInfo = config;
}
strncpy用于截取数据,可以在参数一的位置,将参数二到参数三之间的数据截取出来
不过我解析出来的文件里,汉字是乱码,还没搞清楚是为啥
根据key获取value的值
//根据key获取对应value
char* getValueByKey(char* key, struct ConfigInfo* configInfo, int lines)
{
for (int i = 0; i < lines; i++)
{
if (strcmp(configInfo[i].key ,key)== 0)
{
return configInfo[i].value;
}
}
return NULL;
}
释放内存
//释放内存
void freeSpace(struct ConfigInfo* configInfo)
{
if (configInfo == NULL)
{
return;
}
free(configInfo);
configInfo == NULL;
}
文件加密
需求:
加密:
- 数据左移4位
- 用或运算使所有数变成负数
- 加上(0000~1111)之间的随机数
解密:
- 左移1位
- 右移5位
feof是C语言标准库函数,其原型在stdio.h中,其功能是检测流上的文件结束符,如果文件结束,则返回非0值,否则返回0
加密
void codeFile(const char* sourceFile, const char* destFile)
{
//# 35
//short 0000 0000 0010 0011
// << 4 0000 0010 0011 0000
// | 1000 0010 0011 0000
//随机数 1000 0010 0011 ???? 0~15
FILE* fp1 = fopen(sourceFile, "r");
FILE* fp2 = fopen(destFile, "w");
if (fp1 == NULL || fp2 == NULL)
return;
char ch;
while ((ch = fgetc(fp1)) != EOF)
{
//给每个字符进行加密
short temp = (short)ch;
//左移4位
temp = temp << 4;
//编程负数
temp = temp | 0x8000;
//添加随机数
temp = temp + rand() % 16;
//将加密字符放入目标文件
fprintf(fp2,"%hd\n",temp);
}
fclose(fp1);
fclose(fp2);
}
在加密时,虽然有在代码中加入随机数,但是加密出来的结果依然每次都一样
这时需要给随机数一个随机数种子,我这里使用的是以时间作为基准的
#include <time.h>
//随机数种子
srand((unsigned int)time(NULL));
%hd 代表输出一个短整数,对应上面的short数据类型
解密
void decodeFile(char* sourceFile, char* destFile)
{
//<<1 000 0010 0011 ????0
//>>5 0000 0000 0010 0011
FILE* fp1 = fopen(sourceFile, "r");
FILE* fp2 = fopen(destFile, "w");
if (fp1 == NULL || fp2 == NULL)
return;
short temp;
while (!feof(fp1))
{
fscanf(fp1, "%hd", &temp);
//temp = temp << 1
temp <<= 2;
temp >>= 5;
char ch = (char)temp;
fputc(ch, fp2);
}
fclose(fp1);
fclose(fp2);
}
fscanf从输入流中读入数据,存储到对应的argument中,遇到空格和换行时结束,这里的temp需要是他的指针
函数指针
函数指针基本介绍
- 函数指针 指向函数入口地址的指针
- 函数名 函数的入口地址
函数指针写法:返回值类型 +(指针变量名)(形参列表)
func();
qDebug("%d",func);
//函数指针 指向函数地址
int * p = (int *)-775679270;
void (*pFunc)() = (void (*)())p;
pFunc();
函数指针的定义方式
一 、先定义出函数的类型,在通过类型定义函数指针变量
void test01()
{
//先定义出函数的类型,在通过类型定义函数指针变量
typedef void (FUNC_TYPE)(int, char);//定义出一个函数类型,返回值是void,形参列表(int,char);
FUNC_TYPE* pFunc = func;
pFunc(10, 'a');
}
二、先定义出函数指针的类型,再通过类型定义函数指针变量
void test02()
{
//先定义出函数指针的类型,再通过类型定义函数指针变量
typedef void (*FUNC_TYPE)(int, char);
FUNC_TYPE pFunc = func;
pFunc(20, 'c');
}
三、直接定义函数指针变量(最重要的)
void test03()
{
//直接定义函数指针变量
void(* p)(int,char) = func;
p(30, 'd');
}
四、函数指针数组的定义方式
//函数指针数组
void func1()
{
printf("func1的调用\n");
}
void func2()
{
printf("func2的调用\n");
}
void func3()
{
printf("func3的调用\n");
}
void test04()
{
//函数指针数组的定义方式
void (*func_array[3])();
func_array[0] = func1;
func_array[1] = func2;
func_array[2] = func3;
for (int i = 0; i < 3; i++)
{
func_array[i]();
}
}
函数指针做函数参数 —回调函数
回调函数案例1 打印任意类型数据
提供函数 可以打印任意类型数据
参数一 数据地址 参数二 函数指针
//提供一个函数,可以打印任意类型数据
void myPrint(void * data,void(*mp)(void *))
{
//int* num = data;
//printf("%d\n", num);
mp(data);
}
struct Person
{
char name[64];
int age;
};
void printInt(void * data)
{
int* num = data;
printf("%d\n", *num);
}
void printDouble(void* data)
{
double* num = data;
printf("%f\n", *num);
}
void printPerson(void* data)
{
struct Person * num = data;
printf("姓名:%s 年龄:%d\n", num->name,num->age);
}
void test01()
{
int a = 10;
myPrint(&a,printInt);
double b = 3.14;;
myPrint(&b,printDouble);
struct Person p1 = { "aaa",18 };
myPrint(&p1, printPerson);
}
回调函数案例2 打印任意类型数组
//参数1 传入数组首地址 参数2 数组中每个元素占用内存 参数3 数组长度
void printAllArray(void * arr,int eleSize,int len,void(*myPrint)(void *))
{
char* p = arr;//利用p指针接收数组首地址
for (int i = 0; i < len; i++)
{
//获取每个元素的首地址
char* pAddr = p + eleSize * i;
//printf("%d\n", *(int*)pAddr);
myPrint(pAddr);
}
}
回调函数案例3 查找数组中的元素
查找数组中指定元素是否存在,如果存在返回1,否则返回0,
利用回调函数 做元素的对比
int findArrayEle(void * array,int eleSize,int len,void *data,int (*myCompare)(void*,void *))
{
char* p = array;
for (int i = 0; i < len; i++)
{
//获取到每个元素的首地址
char* eleAddr = p + eleSize*i;
//if(用户传入的数据 == 数组中遍历的元素)
//if (data == eleAddr)错误的,不可以直接用地址做比较
if(myCompare(data,eleAddr))
{
return 1;
}
}
return 0;
}
int comparePerson(void * data1,void *data2)
{
struct Person* p1 = data1;
struct Person* p2 = data2;
//如果姓名相等 并且年龄相等 返回真,否则返回假
if (strcmp(p1->name,p2->name) && p1->age == p2->age)
{
return 1;
}
else
return 0;
}
void test03()
{
//查找数组中是否有指定类型的元素,如果有返回1,如果没有返回0
struct Person personArray[] =
{
{"aaa",18},
{"bb",19},
{"cccc",20},
{"dddddd",21},
};
int len = sizeof(personArray) / sizeof(struct Person);
struct Person p = { "ccc",20 };
int ret = findArrayEle(personArray, sizeof(struct Person), len, &p, comparePerson);
if (ret)
{
printf("找到了元素\n");
}
else {
printf("未找到元素\n");
}
}