C基础梳理 2019.1.3(结构体嵌套二级指针,结构体偏移量,文件读取,文件写入,配置文件读写,c接口的写法)

结构体嵌套二级指针

struct Teacher{
    char* name;
    char** student;
}

 

 

 

结构体偏移量

头文件

#include<stddef.h>

中的宏函数offsetof,可以获取成员距离首地址的偏移量(下面的代码就能拿到a2的首地址)

struct A{
    char a1;
    int a2;
}

void test(){
    struct A a={'b', 20};
    (char*)&a+offsetof(struct A, a2);

}

 

单个结构体示例

#include<stdio.h>
#include<stddef.h>

struct A{
	char a1;
	int a2;
};

void test(){
	struct A a={'b', 20};
	printf("A.a2:%d\n", *(int*)((char*)&a+offsetof(struct A, a2)));
}

int main(){
	test();
	return 0;
}

运行结果

 

 

结构体嵌套

希望获取struct C里面的b的值

#include<stdio.h>
#include<stddef.h>

struct C{
	char a;
	double b;
};

struct B{
	int a;
	char b;
	struct C c;
};

void test(){
	struct B b={'a', 20, 30, 3.14};
	
	int off1=offsetof(struct B, c);
	int off2=offsetof(struct C, b);

	printf("%f\n", *(double*)(((char *)&b+off1)+off2));
	printf("%d\n", &(b.c.b));
	printf("%f\n", ((struct C *)((char *)&b + off1))->b);

}

int main(){
	test();
	return 0;
}

运行结果

 

 

 

文件操作

文件概念

文件是磁盘一段命名的存储区。

fopen()如果成功了,会返回一个FIFL结构体。

 

读写文件的时候,我们到底是从哪里读的?

读写文件,最先读写的是缓冲区

 

 

 

文本方式和二进制方式

 

 

 

文件打开and读取内容

#include<stdio.h>
#include<stddef.h>
#include<stdlib.h>

void test(){
	FILE *f =fopen("./test.txt","r");
	if(f==NULL){
		printf("打开文件失败\n");
		return;
	}

	char ch;
	while(!feof(f)){
		//一个字节一个字节的读
		ch=fgetc(f);

		//如果读到文件结束符,则结束循环。否则会输出文件结尾符
		if(feof(f)) break;

		printf("%c", ch);
	}

	fclose(f);
	f=NULL;
}

int main(){
	test();
	return 0;
}

运行结果

 

或者也可以这样写

#include<stdio.h>
#include<stddef.h>
#include<stdlib.h>

void test(){
	FILE *f =fopen("./test.txt","r");
	if(f==NULL){
		printf("打开文件失败\n");
		return;
	}

	char ch;
	while((ch=fgetc(f))!=EOF){
		printf("%c", ch);
	}

	fclose(f);
	f=NULL;
}

int main(){
	test();
	return 0;
}

 

 

 

配置文件读写

配置文件的规则如图

只有类似 port:8080 这样的才算是有效行。上图中一共六个有效行。我们要写一个工具用于读取and解析配置文件中的参数信息。

 

 

1、定义接口  ConfigFile.h

写一个C里面的接口,如果不写下面的这个,c++没办法调用你写的这个c函数

cpp里不用写这,c语言里面要写

//防止头文件重复包含
#pragma once

//目的:为了在c++中能够调用c写的函数
#ifdef __cplusplus
	extern "C"{
#endif



#ifdef __cplusplus
}
#endif

 

完整的接口文件

//防止头文件重复包含
#pragma once

#include<stdio.h>

struct ConfigInfo {
	char key[64];
	char val[128];
};

#ifdef __cplusplus
extern "C" {
#endif
	//获取文件有效行数(即不包括注释,不包括空行,不包括无用的数据)
	int getLines_ConfigFile(FILE *file);

	//加载配置文件
	void loadFile_ConfigFile(const char* filePath, char ***fileData, int *lines);

	//解析配置文件
	void parseFile_ConfigFile(char **fileData, int lines, struct ConfigInfo** info);

	//获得指定配置信息
	char* getInfo_ConfigFile(const char *key, struct ConfigInfo *info, int line);

	//释放文件信息
	void destoryInfo_ConfigFile(struct ConfigInfo *info);

	//判断当前行是否有效
	int isValid_ConfigFile(const char* buf);

#ifdef __cplusplus
}
#endif

 

2、ConfigFile.c

先把.h定义的函数都弄过来

#include"configFile.h"

//获取文件有效行数(即不包括注释,不包括空行,不包括无用的数据)
int getLines_ConfigFile(FILE *file);

//加载配置文件
void loadFile_ConfigFile(const char* filePath, char ***fileData, int *lines);

//解析配置文件
void parseFile_ConfigFile(char **fileData, int lines, struct ConfigInfo** info);

//获得指定配置信息
char* getInfo_ConfigFile(const char *key, struct ConfigInfo *info, int line);

//释放文件信息
void destoryInfo_ConfigFile(struct ConfigInfo *info);

//判断当前行是否有效
int isValid_ConfigFile(const char* buf);

然后开始一个一个的写函数实现

 

3、首先写第二个函数(加载配置文件)

//加载配置文件
void loadFile_ConfigFile(const char* filePath, char ***fileData, int *line) {
	//读取文件
	FILE *file = fopen(filePath, "r");
	if (file == NULL) return;

	int lines = getLines_ConfigFile(file);
	//开辟一个数组存储char*指针
	//给每行数据开辟内存
	char**temp = malloc(sizeof(char*)*lines);

	char buf[1024] = { 0 };
	int index = 0;
	//读取每行数据
	while (fgets(buf, 1024, file) != NULL) {
		//判断读取的是不是有效行
		if (!isValid_ConfigFile(buf)) {
			continue;
		}
		//有的有换行,有的没有,所以对于每一行有效数据,我们先收集起来,之后再去处理
		//因此先 strlen(buf) + 1
		temp[index++] = malloc(strlen(buf) + 1);
		strcpy(temp[index], buf);
		++index;
		memset(buf, 0, 1024);
	}

	//关闭文件
	fclose(file);

	//将读取的数据和获取的行数存储起来
	*fileData = temp;
	*line = lines;

}

 

 

4、再把获取文件行数的函数写一下

//获取文件有效行数(即不包括注释,不包括空行,不包括无用的数据)
int getLines_ConfigFile(FILE *file){
	char buf[1024]={0};
	int lines=0;

	while(fgets(buf,1024,file)!=NULL){
		//判断是否为有效行
		if(!isValid_ConfigFile(buf)) continue;
		//清空buf
		memset(buf, 0, 1024);
		lines++;
	}

	//把文件指针重置到文件开头
	fseek(file, 0, SEEK_SET);

	return lines;
}

 

 

5、再写一下判断当前行是否是有效行

//判断当前行是否有效
int isValid_ConfigFile(const char* buf){
	if(buf[0]=='#'|| buf[0]=='\n'|| strchr(buf,':')==NULL)
		return 0;

	return 1;
}

 

 

6、然后在有main函数的那个文件里进行测试

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#include"config.h"

void test(){
	char **fileData=NULL;
	int lines=0;
	loadFile_ConfigFile("./test.txt", &fileData, &lines);
	printf("lines=%d\n",lines);
}

int main(){
	test();

	return 0;
}

 

 

7、解析获取到的配置文件中的有效行

//解析配置文件
void parseFile_ConfigFile(char **fileData, int lines, struct ConfigInfo** info){
	struct ConfigInfo *myinfo=malloc(sizeof(struct ConfigInfo) *lines);

	//初始化
	memset(myinfo, 0, sizeof(struct ConfigInfo) *lines);
	
	for(int i=0; i<lines; ++i){
		//查找当前有效行的“:”在哪里
		char *pos=strchr(fileData[i], ':');
		//拷贝数据
		strncpy(myinfo[i].key, fileData[i], pos-fileData[i]);
		strncpy(myinfo[i].val, pos+1, strlen(pos+1)-1);
	}

	//释放文件信息
	for(int i=0; i<lines; ++i){
		if(fileData[i]!=NULL){
			free(fileData[i]);
			fileData[i]=NULL;
		}
	}
	*info=myinfo;
}

 

 

8、修改main函数进行测试

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#include"config.h"

void test(){
	char **fileData=NULL;
	int lines=0;
	struct ConfigInfo *info=NULL;

	//加载配置文件
	loadFile_ConfigFile("./test.txt", &fileData, &lines);
	printf("lines=%d\n",lines);

	//解析配置文件
	parserFile_ConfigFile(fileData, lines, &info);
}

int main(){
	test();


	return 0;
}

 

 

 

9、获取指定配置信息

//获得指定配置信息
char* getInfo_ConfigFile(const char *key, struct ConfigInfo *info, int line){
	for(int i=0; i<line; i++){
		if(strcmp(key, info[i].key)==0){
			return info[i].val;
		}
	}

	//没找到要找的key
	return NULL;
}

 

 

10、检测信息提取情况

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#include"config.h"

void test(){
	char **fileData=NULL;
	int lines=0;
	struct ConfigInfo *info=NULL;

	//加载配置文件
	loadFile_ConfigFile("./test.txt", &fileData, &lines);
	printf("lines=%d\n",lines);

	//解析配置文件
	parserFile_ConfigFile(fileData, lines, &info);

	//获取key=ip的val
	printf("IP:%s\n",getInfo_Configfile("ip", info, lines));
	

}

int main(){
	test();


	return 0;
}

 

 

11、释放文件信息

//释放文件信息
void destoryInfo_ConfigFile(struct ConfigInfo *info){
	if(info==NULL) return;

	free(info);
	info=NULL;
}

 

 

各个文件完整版

config.h

//防止头文件重复包含
#pragma once
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

struct ConfigInfo {	char key[64];	char val[128];};

#ifdef __cplusplus
	extern "C"{
#endif
	int getLines_ConfigFile(FILE *file);
	void loadFile_ConfigFile(const char* filePath, char ***fileData, int *line);
	void parseFile_ConfigFile(char **fileData, int lines, struct ConfigInfo** info);
	char* getInfo_ConfigFile(const char *key, struct ConfigInfo *info, int line);
	void destoryInfo_ConfigFile(struct ConfigInfo *info);
	int isValid_ConfigFile(const char* buf);

#ifdef __cplusplus
}
#endif

 

config.c

#include"config.h"
#include<string.h>

//获取文件有效行数(即不包括注释,不包括空行,不包括无用的数据)
int getLines_ConfigFile(FILE *file){
	char buf[1024]={0};
	int lines=0;

	while(fgets(buf,1024,file)!=NULL){
		//判断是否为有效行
		if(!isValid_ConfigFile(buf)) continue;
		//清空buf
		memset(buf, 0, 1024);
		lines++;
	}

	//把文件指针重置到文件开头
	fseek(file, 0, SEEK_SET);

	return lines;
}

//加载配置文件
void loadFile_ConfigFile(const char* filePath, char ***fileData, int *line) {
	//读取文件
	FILE *file = fopen(filePath, "r");
	if (file == NULL) return;

	int lines = getLines_ConfigFile(file);
	//开辟一个数组存储char*指针
	//给每行数据开辟内存
	char**temp = malloc(sizeof(char*)*lines);

	char buf[1024] = { 0 };
	int index = 0;
	//读取每行数据
	while (fgets(buf, 1024, file) != NULL) {
		//判断读取的是不是有效行
		if (!isValid_ConfigFile(buf)) {
			continue;
		}
		//有的有换行,有的没有,所以对于每一行有效数据,我们先收集起来,之后再去处理
		//因此先 strlen(buf) + 1
		temp[index++] = malloc(strlen(buf) + 1);
		strcpy(temp[index], buf);
		++index;
		memset(buf, 0, 1024);
	}

	//关闭文件
	fclose(file);

	//将读取的数据和获取的行数存储起来
	*fileData = temp;
	*line = lines;

}

//解析配置文件
void parseFile_ConfigFile(char **fileData, int lines, struct ConfigInfo** info){
	struct ConfigInfo *myinfo=malloc(sizeof(struct ConfigInfo) *lines);

	//初始化
	memset(myinfo, 0, sizeof(struct ConfigInfo) *lines);
	
	for(int i=0; i<lines; ++i){
		//查找当前有效行的“:”在哪里
		char *pos=strchr(fileData[i], ':');
		//拷贝数据
		strncpy(myinfo[i].key, fileData[i], pos-fileData[i]);
		strncpy(myinfo[i].val, pos+1, strlen(pos+1)-1);
	}

	//释放文件信息
	for(int i=0; i<lines; ++i){
		if(fileData[i]!=NULL){
			free(fileData[i]);
			fileData[i]=NULL;
		}
	}
	*info=myinfo;
}

//获得指定配置信息
char* getInfo_ConfigFile(const char *key, struct ConfigInfo *info, int line){
	for(int i=0; i<line; i++){
		if(strcmp(key, info[i].key)==0){
			return info[i].val;
		}
	}

	//没找到要找的key
	return NULL;


}
//释放文件信息
void destoryInfo_ConfigFile(struct ConfigInfo *info){
	if(info==NULL) return;

	free(info);
	info=NULL;
}

//判断当前行是否有效
int isValid_ConfigFile(const char* buf){
	if(buf[0]=='#'|| buf[0]=='\n'|| strchr(buf,':')==NULL)
		return 0;

	return 1;
}

 

parse.c

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#include"config.h"

void test(){
	char **fileData=NULL;
	int lines=0;
	struct ConfigInfo *info=NULL;

	//加载配置文件
	loadFile_ConfigFile("./test.txt", &fileData, &lines);
	printf("lines=%d\n",lines);

	//解析配置文件
	parserFile_ConfigFile(fileData, lines, &info);

	//获取key=ip的val
	printf("IP:%s\n",getInfo_Configfile("ip", info, lines));
	
	//释放空间
	destoryinfo_ConfigFile(info);
}

int main(){
	test();


	return 0;
}

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值