终点亦是起点[C完结]这一刻 我也变成了光

来深入一下长度不受限制的字符串函数
//1. strlen字符串长度函数  

#include<stdio.h>
#include<string.h>
int main() {
	if (strlen("abc") - strlen("abcdef") > 0) {
		printf(">\n");//返回值是无符号类型   size_t(unsigned int)     所以打印结果是>
	}
	else {
		printf("<\n");
	}
	return 0;
}

//  模拟实现strlen函数

#include<stdio.h>
int my_strlen(const char* array) {//计算变量的长度 不修改 const
	assert(array != NULL);//让代码更加健康
	int count = 0;//计数器实现strlen
	while (*array != '\0') {
		count++;
		array++;
	}
	return count;
}
int main() {
	char array[] = "abc";
	int much = my_strlen(array);
	printf("%d\n", much);
	return 0;
}

//2.  strcpy拷贝函数    string字符串   copy拷贝

#include<stdio.h>
 #include<string.h>
int main() {
	char array[20] = "###################";
	//strcpy(array, "hello");//这么写没毛病  但有点小别扭
	char array_one[] = "hello";
	strcpy(array, array_one);
	printf("%s\n", array);//调试你会发现  '\0'  也拷贝进去了
	return 0;
}

//3.  strcat字符串追加  追加时先找到目标文件的  '\0'  再把源数据追加进去

#include<stdio.h>
#include<string.h>
int main() {
	char array1[30] = "hello \0#########";
	char array2[] = "world";
	strcat(array1, array2);
	printf("%s\n", array1);//调试你会发现  '\0'  也追加进去了  并且在 \0 后面追加的
	return 0;
}

//模拟实现  strcat

#include<stdio.h>
#include<assert.h>
char* my_strcat(char* array, const char* array1) {//追加又不修改  更加健康
	char* start = array;//追加函数完成后会返回目标文件起始地址
	assert(array && array1);//断言一下 预防空指针
	//1.找到目标的  '\0'
	while (*array) {
		array++;
	}
	//2.追加字符串,包含  '\0'
	while (*array++ = *array1++) {
		;
	}
	return start;
}
int main() {
	char array[20] = "hello";
	char array1[] = "world";
	printf("%s\n", my_strcat(array, array1));
	return 0;
}

//4. 模拟实现字符串比较函数  strcmp

#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* array1,const char* array2) {//  strcmp函数返回大于或小于 或等于的数字
	assert(array1 && array2);//健康
	while (*array1 == *array2) {
		if (*array1 == '\0') {
			return 0;
		}
		array1++;
		array2++;
	}
	if (*array1 > *array2) {
		return 1;
	}
	else {
		return -1;
	}
}
int main() {
	char* array1 = "abcde";
	char* array2 = "able";
	int number = my_strcmp(array1, array2);
	if (number > 0) {
		printf("array1 > array2\n");
	}
	else if (number < 0) {
		printf("array1 < array2\n");
	}
	else {
		printf("array1 = array2\n");
	}
	return 0;
}

//长度受限制的字符串函数  相对较于安全  可控
//1. strncpy拷贝函数

#include<stdio.h>
  #include<string.h>
int main() {
	char array1[20] = "abcdef";
	char array2[] = "ghk";
	strncpy(array1, array2, 5);//不够五个  将\0拷贝过去
	printf("%s\n", array1);
	return 0;
}

//2.strncat 追加函数

#include<stdio.h>
 #include<string.h>
int main() {
	char array1[30] = "hello \0##############";
	char array2[] = "world";
	strncat(array1, array2, 8);//   /0也追加过去
	printf("%s\n", array1);
	return 0;

}

//3.strncmp  你想比较几个字符就比较几个字符


//模拟实现strstr函数  在array1中查找是否有array2数组

#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strstr(const char* array1, const char* array2) {//只找到不修改
	assert(array1 && array2);
	const char* point1 = NULL;
	const char* point2 = NULL;
	char* start = array1;
	if (*array2 == '\0') {
		return array1;
	}
	while (*start) {
		point1 = start;
		point2 = array2;
		while (*point1 && *point2 && (*point1 == *point2)) {
			point1++;
			point2++;
		}
		if (*point2 == '\0') {
			return start;
		}
		start++;
	}
	return NULL;
}
int main() {
	char array1[] = "abcdefabcdef";
	char array2[] = "cdef";
	char* point = my_strstr(array1, array2);
	if (point == NULL) {
		printf("没找到\n");
	}
	else {
		printf("找到了:%s\n", point);
	}
	return 0;
}

//strtok字符串切割函数  找到标记会用\0结尾,返回一个指向这个标记的指针

#include<stdio.h>
#include<string.h>
int main() {
	char array[] = "110120@qq.com";
	char* point = "@.";

	char alternatives[20] = { 0 };
	strcpy(alternatives, array);//strtok函数会改变被操作字符串,所以使用它切分的字符串一般都是临时拷贝的内容并可以修改
	char* point1 = NULL;

	//point = strtok(alternatives, point);
	//printf("第一次切割 %s\n", point);

	//point = strtok(NULL, point);//strtok函数第一个函数为NULL,则在标记处保存的位置开始,找下一个标记
	//printf("第二次切割 %s\n", point);

	//point = strtok(NULL, point);
	//printf("第三次切割 %s\n", point);//如果字符串不存在更多标记,返回NULL指针

	for (point1 = strtok(alternatives, point); point1 != NULL; point1 = strtok(NULL, point)) {
		printf("%s\n", point1);
	}

	return 0;
}

//使用库函数的时候,调用失败都会有错误码  errno
//strerror  错误码转换为错误信息

#include<stdio.h>
#include<string.h>//strerror头文件
#include<errno.h> //errno头文件  全局错误码
#include<stdio.h> //perror头文件

int main() {

	//printf("%s\n", strerror(0));      // No error
	//printf("%s\n", strerror(1));     //Operation not permitted
	//printf("%s\n", strerror(2));     //No such file or directory
	//printf("%s\n", strerror(3));     //No such process
	//printf("%s\n", strerror(4));     //Interrupted function call
	//printf("%s\n", strerror(5));	 //Input / output error

	FILE* point = fopen("test.txt", "r");//打开文件 读的形式打开  打开失败会返回NULL指针
	if (point == NULL) {
		/*printf("%s\n", strerror(errno));*/
		perror("fopen");//   错误码转化为错误信息,打印错误信息  一步到位
		return 1;//调用失败就不用往下走了 程序结束
	}
	fclose(point);//关闭文件
	point = NULL;
	return 0;
}

//讲一下字符分类函数

#include<stdio.h>
#include<ctype.h> //isdigit头文件  
int main() {
	char judge = '#';
	int number = isdigit(judge);//isdigit 如果是数字字符0-9返回非零值  不是返回0
	printf("%d\n", number);
	return 0;
}

//字符转换函数

#include<stdio.h>
#include<ctype.h>//isupper大写字母字符分类函数头文件
int main() {
	char array[20] = { 0 };
	scanf("%s", array);
	int frequency = 0;
	while (array[frequency] != '\0') {
		if (isupper(array[frequency])) {
			array[frequency] = tolower(array[frequency]);//tolower将大写转换为小写
		}
		printf("%c ", array[frequency]);
		frequency++;
	}
	return 0;
}

//内存函数
//内存拷贝  memcpy  拷贝不重叠的内存  VS中重不重叠都可以拷贝   memory记忆在程序中为内存  cpy(copy)复制,拷贝

//   memmove函数可以处理重叠内存 

#include<stdio.h>
#include<string.h>//memcpy头文件
#include<assert.h>
void* my_memcpy(void* target, const void* source, size_t number) {// 我不知道内存拷贝的类型  用void*   ; size_t无符号整数
	void* start = target;
	assert(target && source);
	while (number--) {
		*(char*)target = *(char*)source;
		target = (char*)target + 1;
		source = (char*)source + 1;
	}
	return start;
} 
void* my_memmove(void* target, const void* source, size_t number) {
	void* start = target;
	assert(target && source);
	if (target < source) {//前->后
		while (number--) {
			*(char*)target = *(char*)source;
			target = (char*)target + 1;
			source = (char*)source + 1;
		}
	}
	else {//后->前
		while (number--) {
			*((char*)target + number) = *((char*)source + number);
		}
	}
	return start;
}
int main() {
	/*int array1[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	int array2[10] = { 0 };
	memcpy(array2, array1, 20);*/

	//模拟实现
	int array1[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	int array2[10] = { 0 };
	//my_memcpy(array2, array1, 20);
	//my_memcpy(array1+2, array1, 20);//自己写的不能拷贝重叠的内存

	//memmove(array1 + 2, array1, 20);
	//模拟实现memmove
	my_memmove(array1 + 2, array1, 20);
	return 0;
}

//memcmp 内存比较   memset内存设置

#include<stdio.h>
#include<string.h>//内存函数头文件
int main() {
	int array1[] = { 1, 2, 3, 4, 5 };
	int array2[] = { 1, 1, 4 };
	int judge = memcmp(array1, array2, 8);//  和strcmp字符串比较类似
	printf("%d\n", judge);
	int array3[10] = { 0 };
	memset(array3, 1, 20);//以字节为单位进行内存设置
	return 0;
}

//结构体深刨
//1.只有关键字 没有名字 这种被称为匿名结构体类型

struct person
{
	char name[20];
	int age;
	double height;
	struct//匿名结构体类型
	{
		char phone[12];
		char address[20];
	};
};
#include<stdio.h>
int main() {
	struct person one = { "one",18,180.00,{"110120","中国大陆"} };
	printf("%s\n", one.address);
	return 0;
}//结构体对象 one 可以通过 one.address 直接访问匿名结构体成员变量 phone,代码相对比较简洁
  //反之则必须通过 jim.结构体名字.phone 来访问结构体成员变量

//2.结构体自引用  不是包含同类型的变量,而是包含同类型的指针

//数据结构:数据在内存中存储的结构 ; 线性数据结构有一种是顺序表 就是 1,2,3,4…… 一个挨一个存放;第二个是链表,没有相邻,而是你在这个位置,我在那个位置,每个数据前半段是存放数据的数据域,后半段是指针域(你可以找到我,我也可以找到你)
struct person
{
    char name[20];
    int age;
    struct person* next;//如果用 struct person next,这不是死递归嘛
    //          指针变量大小是可算得,  struct person* next可以找到和自己同类型的结构
}

//3.结构体类型对齐 

#include<stdio.h>
struct student1
{
	char one;//第一个成员在结构体变量偏移量为0的地址处,其他成员变量要对齐到对齐数(编译器默认对齐数与该成员大小的较小值,VS是8)的整数倍处
	int two;
	char three;
};//结构体总大小为最大对齐数的整数倍  如果嵌套了结构体,则找结构体最大对齐数就可以了
struct student2
{
	char one;
	char three;
	int two;
};
int main() {
	struct student1 one = { 0 };
	struct student2 two = { 0 };
	printf("%d\n", sizeof(one));//12
	printf("%d\n", sizeof(two));//8   student1和2的成员一摸一样,但所占总空间还是有些区别的 让占用空间小的尽量集中在一起
	return 0;
}

/*
   为什么存在内存对齐?
   1.平台原因:不是所有的硬件可以访问任意地址的数据;某些硬件只能只能获取特定的数据,否则硬件会出现异常
   2.性能原因:访问未对齐需要两次访问,访问对齐需要一次就可以了
   总的来说:结构体对齐就是用空间来换取时间的做法
*/

//修改默认对齐数  和 offsetof(计算变量在首地址的偏移)宏  模拟实现offsetof

#include<stdio.h>
#include <stddef.h>//offsetof头文件
//#pragma pack(2)//修改默认对齐数为8
struct student1
{
	char one;
	int two;
	char three;
};
//#pragma pack()//修改回来

#define OFFSETOF(struct_name,member_name)     (int*)&(((struct_name*)0)->member_name)

int main() {
	//printf("%d\n", sizeof(struct student1));//8
	//printf("%d\n", offsetof/*小写给人误以为是函数*/(struct student1, one));
	//printf("%d\n", offsetof(struct student1, two));
	//printf("%d\n", offsetof(struct student1, three));

    printf("%d\n", OFFSETOF(struct student1, one));
    printf("%d\n", OFFSETOF(struct student1, two));
    printf("%d\n", OFFSETOF(struct student1, three));

	return 0;
}

/*4.位段 
    1.成员:int   ;  unsigned int  ;signed  int; char 
    2.位段成员名后边有一个冒号和数字
    3.位段的空间是按照4个字节(int)或者1个字节(char)来开辟的

    4.位段不跨平台的:第一int位段当作有符号还是无符号不确定
                     第二位段最大数目不确定(16位机器最大16,32位机器32)
                     第三位段的成员从左向右分配还是从右向左分配尚未定义
                     第四:当一个结构有两个位段,第二个位段比较大,无法容纳第一个位段剩余位时,是舍弃还是利用不确定
    

*/

#include<stdio.h>
struct word
{
    int a : 2;//成员a占2个bit位
    int b : 5;//成员b占5个bit位
    int c : 30;//成员c占30个bit位
};
int main() {
    printf("%d\n", sizeof(struct word));
    return 0;
}

//位段可以更好的节省空间,但有跨平台的问题

/*  枚举的优点:
             1.增加代码的可读性和维护性
             2.有类型检查,更为严谨
             3.便于调试
             4.使用方便,一次可定义多个变量

*/ 

enum Color
{
    RED,
    GREEN,
    BLUE,

};
#include<stdio.h>
int main() {
    enum Color one = BLUE;
    printf("%d\n", RED);
    printf("%d\n", GREEN);
    printf("%d\n", sizeof(one));
    return 0;
}

//联合体(共用体)  特征是 公用同一块空间

union body
{
    char one;
    int two;
}u;
#include<stdio.h>
int BigSmall() {
    u.two = 1;
    return u.one;
}
int main() {
    //union body test;
    //printf("%d\n", sizeof(test));//打印结果是4

    //printf("%p\n", &test);
    //printf("%p\n", &(test.one));
    //printf("%p\n", &(test.two));//地址一样

    int number = BigSmall();
    if (number == 1) {
        printf("小端存储\n");
    }
    else {
        printf("大端存储\n");
    }
    return 0;
}

//联合体大小计算  联合体的大小至少为最大成员的大小 , 当最大成员大小不是最大对齐数的倍数时,要对齐到最大对齐数的倍数
/*
    写一个通讯录  1.可以存放1000个人信息(名字+性别+年龄+电话+地址) 2.增删查改
    AddressBook.h 函数声明    

#pragma once

#include<stdio.h>
#include<stdlib.h>//动态用到的头文件
#include<string.h>

#define MAX_NAME 20
#define MAX_GENDER 10
#define MAX_TELEPHONE 12
#define MAX_ADDRESS 30
#define MAX 1000

//动态版本
#define CAPACITY 3
#define SIZE 2

//联系人定义
typedef struct person
{
	char name[MAX_NAME];
	char gender[MAX_GENDER];
	int age;
	char telephone[MAX_TELEPHONE];
	char address[MAX_ADDRESS];
}person;

//静态通讯录定义
//typedef struct AddressBook
//{
//	person app[MAX];//存放人的信息
//	int size;//记录当前通讯录有效信息个数
//}AddressBook;

//动态通讯录定义
typedef struct AddressBook
{
	person* app;//存放人的信息
	int size;//记录当前通讯录有效信息个数
	int capacity;//记录当前通讯录的最大容量
}AddressBook;


//初始化通讯录
void InitializationAddressBook(AddressBook* point);

//增
void AddPerson(AddressBook* point);

//打印
void PrintfPerson(const AddressBook* point);//打印不修改 代码更健康

//删
void DelPerson(AddressBook* point);

//查
void SearchPerson(AddressBook* point);

//改
void ReviesPerson(AddressBook* point);

//动态释放空间
void FreePerson(AddressBook* point);

//保存通讯录信息
void SavePerson(AddressBook* point);

//加载文件
void LoadAddressBook(AddressBook* point);

//增容函数
void CapacityIncrease(AddressBook* point);


    AddressBook.c 函数定义

#define _CRT_SECURE_NO_WARNINGS
#include"AddressBook.h"

//静态初始化
//void InitializationAddressBook(AddressBook* point) {
//	point->size = 0;
//	memset(point->app, 0, sizeof(point->app));
//}

//动态初始化
void InitializationAddressBook(AddressBook* point) {
	point->app = (person*)malloc(CAPACITY * sizeof(person));
	if (point->app == NULL) {
		perror("InitializationAddressBook");
		return;
	}
	point->size = 0;
	point->capacity = CAPACITY;

	//加载文件
	LoadAddressBook(point);
}

//动态释放
void FreePerson(AddressBook* point) {
	free(point->app);
	point->app = NULL;
	point->size = 0;
	point->capacity = 0;
}

//静态增
//void AddPerson(AddressBook* point) {
//	if (point->size == MAX) {
//		printf("通讯录已满,无法添加\n");
//		return;
//	}
//	//增加
//	printf("请输入名字:->");
//	scanf("%s", point->app[point->size].name);
//	printf("请输入性别:->");
//	scanf("%s", point->app[point->size].gender);
//	printf("请输入年龄:->");
//	scanf("%d", &(point->app[point->size].age));//年龄需要取地址
//	printf("请输入电话:->");
//	scanf("%s", point->app[point->size].telephone);
//	printf("请输入地址:->");
//	scanf("%s", point->app[point->size].address);
//
//	point->size++;
//	printf("增加成功\n");
//}
 

//增容函数
void CapacityIncrease(AddressBook* point) {
	if (point->size == point->capacity) {
		person* point1 = (person*)realloc(point->app, (point->capacity + SIZE) * sizeof(person));
		if (point1 != NULL) {
			point->app = point1;
			point->capacity += SIZE;
			printf("增容成功\n");
		}
		else {
			perror("AddPerson");
			printf("添加联系人失败\n");
			return;
		}
	}
}

//动态增
void AddPerson(AddressBook* point) {
	//增容
	CapacityIncrease(point);
	
	//增加
	printf("请输入名字:->");
	scanf("%s", point->app[point->size].name);
	printf("请输入性别:->");
	scanf("%s", point->app[point->size].gender);
	printf("请输入年龄:->");
	scanf("%d", &(point->app[point->size].age));//年龄需要取地址
	printf("请输入电话:->");
	scanf("%s", point->app[point->size].telephone);
	printf("请输入地址:->");
	scanf("%s", point->app[point->size].address);

	point->size++;
	printf("增加成功\n");
}



//打印
void PrintfPerson(AddressBook* point) {
	//标题打印
	printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "性别", "年龄", "电话", "地址");
	//打印数据
	int frequency = 0;
	for (frequency = 0; frequency < point->size; frequency++) {
		printf("%-20s\t%-5s\t%-5d\t%-12s\t%-30s\n", 
		    point->app[frequency].name,
			point->app[frequency].gender,
			point->app[frequency].age,
			point->app[frequency].telephone,
			point->app[frequency].address);
	}
}


static int FindName(AddressBook* point, char name[]) {//修饰函数  只在本文件用即可,外部找不到
	int frequency = 0;
	for (frequency = 0; frequency < point->size; frequency++) {
		if (strcmp(point->app[frequency].name, name) == 0) {
			return frequency;
		}
	}
	return -1;
}



//删除
void DelPerson(AddressBook* point) {
	if (point->size == 0) {
		printf("通讯录为空,无需删除\n");
		return;
	}
	char name[MAX_NAME] = { 0 };
	printf("请输入删除人的名字:->");
	scanf("%s", name);
	//有没有删除人的信息
	int who = FindName(point, name);
	if (who == -1) {
		printf("删除人不存在\n");
		return;
	}
	//删除
	int frequency = 0;
	for (frequency = 0; frequency < point->size - 1; frequency++) {
		point->app[frequency] = point->app[frequency + 1];
	}
	point->size--;
	printf("删除成功\n");
}


//查找
void SearchPerson(AddressBook* point) {
	char name[MAX_NAME] = { 0 };
	printf("请输入查找人的名字:->");
	scanf("%s", name);
	//有没有查找人的信息
	int who = FindName(point, name);
	if (who == -1) {
		printf("查找人不存在\n");
		return;
	}
	else {
		printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "性别", "年龄", "电话", "地址");
		printf("%-20s\t%-5s\t%-5d\t%-12s\t%-30s\n",
			point->app[who].name,
			point->app[who].gender,
			point->app[who].age,
			point->app[who].telephone,
			point->app[who].address);
	}
}

//改
void ReviesPerson(AddressBook* point) {
	char name[MAX_NAME] = { 0 };
	printf("请输入修改人的名字:->");
	scanf("%s", name);
	//有没有修改人的信息
	int who = FindName(point, name);
	if (who == -1) {
		printf("修改人不存在\n");
		return;
	}
	else {
		printf("请输入名字:->");
		scanf("%s", point->app[who].name);
		printf("请输入性别:->");
		scanf("%s", point->app[who].gender);
		printf("请输入年龄:->");
		scanf("%s", &(point->app[who].age));//年龄需要取地址
		printf("请输入电话:->");
		scanf("%s", point->app[who].telephone);
		printf("请输入地址:->");
		scanf("%s", point->app[who].address);
		printf("修改成功\n");
	}
}

void SavePerson(AddressBook* point)
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL) {
		perror(SavePerson);
		return;
	}
	//写
	int frequency = 0;
	for (frequency = 0; frequency < point->size; frequency++) {
		fwrite(point->app + frequency, sizeof(person), 1, pf);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
}

//加载文件
void LoadAddressBook(AddressBook* point) {

	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL) {
		perror(LoadAddressBook);
		return;
	}

	//读文件
	person middle = { 0 };
	while (fread(&middle, sizeof(person), 1, pf)) {
		CapacityIncrease(point);//是否考虑增容
		point->app[point->size] = middle;
		point->size++;
	}

	//关闭文件
	fclose(pf);
	pf = NULL;

}


    源.c 通讯录模板

//通讯录--静态版本
#include"AddressBook.h"
//通讯录--动态版本  1.通讯录初始化后,能存放三个人信息  放满后可以增加两个人信息
void menu()
{
    printf("############ 1.ADD  #############\n");
    printf("############ 2.DEL  #############\n");
    printf("############3.SEARCH#############\n");
    printf("############4.REVIES#############\n");
    printf("############5.PRINTF#############\n");
    printf("############ 0.QUIT #############\n");
}
enum Options
{
    QUIT,
    ADD,
    DEL,
    SEARCH,
    REVIES,
    PRINTF
};
int main()
{
    int input = 0;
    AddressBook  ab;//创建通讯录
    InitializationAddressBook(&ab);//初始化通讯录
    do {
        menu();
        printf("请选择:->\n");
        scanf("%d", &input);
        switch (input)
        {
        case ADD:
            //增加人的信息
            AddPerson(&ab);
            break;
        case DEL:
            //删除
            DelPerson(&ab);
            break;
        case SEARCH:
            //查找
            SearchPerson(&ab);
            break;
        case REVIES:
            //修改
            ReviesPerson(&ab);
            break;
        case PRINTF:
            //打印
            PrintfPerson(&ab);
            break;
        case QUIT:
            SavePerson(&ab);//保存信息
            FreePerson(&ab);//动态释放空间
            printf("退出通讯录\n");
            break;
        default:
            printf("选择错误,请重新选择\n");
            break;
        }
    } while (input);
    return 0;
}

//-------------动态内存分配(比固态更灵活,动态内存开辟在堆区)

#include<stdio.h>
#include<stdlib.h>//malloc(开辟空间值未被初始化) ,calloc(值被初始化)和realloc(动态内存空间调整)头文件
int main() {
    //int array[10] = { 0 };//栈区开辟的10个整形空间
    //int* point = (int*)malloc(10 * sizeof(int));//动态内存开辟  因为malloc返回类型是void*所以强制类型转换
    //int* point1 = (int*)realloc(NULL, 40);//这个类似与malloc,直接开辟四十个字节
    int* point = (int*)calloc(10, sizeof(int));

    if (point == NULL) {//开辟失败返回空指针  所以malloc的返回值要做检查
         //常见的动态内存错误:1.对NULL指针解引用操作
        perror("main");
        return 0;
    }

    //使用
    int frequency = 0;
    for (frequency = 0; frequency < 10; frequency++) { //常见的动态内存错误:2.对动态内存空间的越界访问
        printf("%d \n", point[frequency]);
        *(point + frequency) = frequency;
        printf("%d ", point[frequency]);
        
    }

    //调整空间  再加点
    int* start = realloc(point, 10 * sizeof(int));//realloc返回值为调整之后的内存地址(后面有足够的空间直接追加,原来数据不发生变化;如果没有足够的空间,则找一个可以装下的,返回一个新的内存地址)
    if (start != NULL) {//调整失败返回空指针
        point = start;
    }
  
    //回收
    free(point);//malloc和free成对出现  free如果参数是NULL,则函数什么事情都不做
     //常见的动态内存错误:3.free释放非动态开辟空间或只是放一部分(动态开辟的的指针往后走,释放指针之后的动态空间);对同一块动态开辟的空间多次释放或忘记释放(内存泄漏)
     //动态开辟的空间,两种回收方式(1.主动释放2.程序结束)
    point = NULL;//手动置为空指针
    return 0;
}

//温馨提示:静态库又叫数据段   常量放在代码段

//柔性数组(C99 结构体最后一个成员是未知大小的数组) 柔性数组前面必须至少有一个成员

#include<stdio.h>
#include<stdlib.h>
struct test
{
    int number;
    //int array[];//大小是未知
    int array[0];//也可以写成这样
    
};
int main()
{
    //printf("%d\n", sizeof(struct test));// 结果是4  sizeof不计算柔性数组的内存

    struct test* point = (struct test*)malloc(sizeof(struct test) + 10 * sizeof(int));
    point->number = 10;
    int frequency = 0;
    for (frequency = 0; frequency < 10; frequency++) {
        point->array[frequency] = frequency;
    }
    struct test* point1 = (struct test*)realloc(point,sizeof(struct test) + 20 * sizeof(int));
    if (point1 != NULL) {
        point = point1;
    }
    free(point);
    point = NULL;
    return 0;
}

 //牛刀小试
//1.打印X型图案

#include<stdio.h>
int main()
{
    int number = 0;
    while (scanf("%d", &number) != EOF) {
        int frequency1 = 0;
        for (frequency1 = 0; frequency1 < number; frequency1++) {
            int frequency2 = 0;
            for (frequency2 = 0; frequency2 < number; frequency2++) {
                if (frequency1 == frequency2) {
                    printf("*");
                }
                else if (frequency1 + frequency2 == number - 1) {
                    printf("*");
                }
                else {
                    printf(" ");
                }
            }
            printf("\n");
        }
    }
    return 0;
}

//2.获取月份天数

#include<stdio.h>
int main()
{
    int year = 0;
    int month = 0;
    int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,31 };
    while (scanf("%d %d", &year, &month) != EOF) {
        int day = days[month];
        if (month == 2) {
            if ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0)) {
                day ++;
            }
        }
        printf("%d\n", day);
    }
    return 0;
}

//3.五科成绩去掉最高和最低求每科平均值

#include<stdio.h>
int main()
{
    int sum = 0;
    int score = 0;
    int frequency = 0;
    int max = 0;
    int min = 100;
    for (frequency = 0; frequency < 7; frequency++) {
        scanf("%d", &score);
        sum += score;
        if (score > max) {
            max = score;
        }
        if (score < min) {
            min = score;
        }
    }
    printf("%2f\n", (sum - min - max) / 5.0);
    return 0;
}

//4.一个升序数列,插入一个数,仍是升序

#include<stdio.h>
int main()
{
    int array[51] = { 0 };
    int number = 0;
    //输入数据
    scanf("%d", &number);
    int frequency = 0;
    for (frequency = 0; frequency < number; frequency++) {
        scanf("%d", &array[frequency]);
    }
    //输入要插入的数据
    int insert_number = 0;
    scanf("%d", &insert_number);
    for (frequency = number - 1; frequency >= 0; frequency--) {
        if (array[frequency] > insert_number) {
            array[frequency + 1] = array[frequency];
        }
        else {
            array[frequency + 1] = insert_number;
            break;
        }
    }
    if (frequency < 0) {
        array[0] = insert_number;
    }
    //输出
    for (frequency = 0; frequency < number + 1; frequency++) {
        printf("%d ", array[frequency]);
    }
    return 0;
}

//5.将一个数的二进制位的奇数和偶数位的交换

#include<stdio.h>
int main()
{
    int number = 10;
    int middle = ((number & 0xaaaaaaaa) >> 1) + ((number & 0x55555555) << 1);
    printf("%d\n", middle);
    return 0;
}

/*  
    文件:将数据放在电脑硬盘上,做到数据持久化
     磁盘上的文件是文件;在程序设计中有两种(1.程序文件(源程序文件.c)(目标文件.obj)(可执行程序.exe)2.数据文件:程序运行时读写的数据)
     文件名:c:\code(<-文件路径)\test(<-文件主干名).txt(文件后缀)
     一般来说通过FILE的指针来维护FILE结构的变量(文件信息区),定义pf是一个指向FILE类型数据的指针变量,通过文件信息区中的信息来访问该文件
     文件读写之前应先打开文件(fopen),使用结束后应关闭文件(fclose)

*/

#include<stdio.h>
struct student
{
    int age;
    char name[20];
};
int main()
{
    char array[20] = { 0 };
    struct student one = { 18,"aoteman" };
    struct student two = { 0 };

    //打开文件
    //FILE* pf = fopen("D:\\ShiWeiGeng\\新建文件夹 (11)\\VsCode\\Project1\\test.txt", "r");//绝对路径
    FILE* pf = fopen("test.txt", "r");//相对路径
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }

    //写文件  -------------------------
    
    //流(水流):高度抽象的概念;屏幕,硬盘,U盘,光盘,软盘每个都有自己的读写规则,所以为了降低门槛,用流来进行读写
    //其实C语言程序,只要运行,就默认打开了3个流   stdin(标准输入流->键盘),stdout(标准输出流->屏幕),stderr(标准错误流->屏幕)
    
    //1.字符输出函数	fputc	所有输出流
    /*fputc('h', pf);
    fputc('e', pf);
    fputc('l', pf);
    fputc('l', pf);
    fputc('o', pf);*/

    /*fputc('h', stdout);//从屏幕上写入
    fputc('e', stdout);
    fputc('l', stdout);
    fputc('l', stdout);
    fputc('o', stdout);*/

    //2.文本行输出函数	fputs	所有输出流
    /*fputs("abcdefg\n", pf);
    fputs("hijklmn\n", pf);*/

    //3.格式化输出函数	fprintf	所有输出流
    /*fprintf(pf, "%d %s", one.age, one.name);*/
    //fprintf写成printf -> fprintf(stdout, "%d %s", one.age, one.name)   参数不同功能不同

    //4.二进制输出	fwrite	文件
    //fwrite(&one, sizeof(struct student), 1, pf);//字符串用二进制写进去和文本写进去一样

    //读文件---------------------------

    //1.字符输入函数	fgetc	所有输入流  //文件结束或遇到错误会返回EOF
    /*int number=fgetc(pf);
    printf("%c\n", number);
    number = fgetc(pf);
    printf("%c\n", number);
    number = fgetc(pf);
    printf("%c\n", number);*/

    /*int number = fgetc(stdin);//从键盘上读取
    printf("%c\n", number);
    number = fgetc(stdin);
    printf("%c\n", number);
    number = fgetc(stdin);
    printf("%c\n", number);*/

    //2.文本行输入函数	fgets	所有输入流
    //fgets(array, 4, pf);//读取3个,另外一个放\0
    //printf("%s\n", array);
    //fgets(array, 4, pf);
    //printf("%s\n", array);

    //3.格式化输入函数	fscanf	所有输入流
    /*fscanf(pf, "%d %s", &(two.age), two.name);
    //fscanf写成scanf-> fscanf(stdin, "%d %s", &(two.age), two.name);
    printf("%d %s\n", two.age, two.name);*/

    //4.二进制输入	fread	文件
    /*fread(&two, sizeof(struct student), 1, pf);
    printf("%d %s\n", two.age, two.name);*/

    //文件的随机读写--------------------------------------------
    
    //  1.  fseek根据文件指针的位置和偏移来定位文件指针
    //  2.ftell返回文件指针起始位置的偏移量

    fseek(pf, 2, SEEK_CUR);//  SEEK_CUR  当前位置   ; SEEK_END  最后位置  ; SEEK_SET  最前位置
    int number = fgetc(pf);
    printf("%c\n", number);
    int number1 = ftell(pf);
    printf("%c\n", number);

    fseek(pf, -2, SEEK_END);
    number = fgetc(pf);
    printf("%c\n", number);
    number1 = ftell(pf);
    printf("%c\n", number);

    fseek(pf, 2, SEEK_SET);
    number = fgetc(pf);
    printf("%c\n", number);
    number1 = ftell(pf);
    printf("%c\n", number);

    //  3.rewind文件指针的位置返回到起始位置

    rewind(pf);
    number = fgetc(pf);
    printf("%c\n", number);


    //关闭文件-------------------------------
    fclose(pf);
    pf = NULL;
    return 0;
}//      内存程序   (输入/读取)<----------------------------->(输出/写入)  文件(硬盘)

//sprintf  和sscanf

struct student
{
    char name[20];
    int age;
    char gender[10];
};
#include<stdio.h>
int main()
{
    struct student one = { "aoteman",18,"weizhi" };
    struct student middle1 = { 0 };
    char middle2[100] = { 0 };

    //把一个格式化的数据转化为字符串
    sprintf(middle2, "%s %d %s", one.name, one.age, one.gender);
    printf("%s\n", middle2);

    //从middle2中还原一个结构体数据
    sscanf(middle2, "%s %d %s", middle1.name, &(middle1.age), middle1.gender);
    printf("%s %d %s\n", middle1.name, middle1.age, middle1.gender);
    return 0;
        
}

/*  文件相关知识
1. 一组函数对比
    scanf      针对标准输入的格式化输入语句  stdin
    fscanf     针对所有输入流的格式化输入语句  stdin/文件
    sscanf     从一个字符串中读取一个格式化数据

    printf     针对标准输出的格式化输入语句  stdout
    fprintf    针对所有输入流的格式化输入语句  stdout/文件
    sprintf    把一个格式化的数据,转换为字符串


 2. 数据在在内存中以二进制形式存储,不加转换输出到外存,就是二进制文件(数值既可以二进制存储也可以ASCII形式存储)
   外存以ASCII形式存储的文件就是文本文件(字符)

   比如10000这个数字  ASCII形式存储,需要五个字节(每个字符一个字节);而二进制形式输出,则只要四个字节

 3.文件缓冲区  想磁盘中输出数据先放到文件缓冲区 装满后才一起放到磁盘  提高了效率


 4.文件读取结束判定  feof(在文件读取中,不能用feof函数返回值直接判断文件是否结束,而是判断读取文件结束还是遇到文件结尾结束)

   fgetc函数在读取结束的时候,会返回EOF  ; 正常读取结束后,返回的是读取到字符串的ASCII值

   fgets函数在读取结束的时候,会返回空指针 ; 正常读取结束后,返回字符串空间的起始地址

   fread函数在读取结束后,返回的是实际读取的个数;如果发现读取的个数小于指定的元素个数,这就是最后一次

*/

#include<stdio.h>
int main()
{
    FILE* pf_read = fopen("test.txt", "r");
    if (pf_read == NULL) {
        return 1;
    }
    FILE* pf_write = fopen("my_test.txt", "w");
    if (pf_write == NULL) {
        //打开这个文件错误,就把上个文件关闭
        fclose(pf_read);
        pf_read = NULL;
        return 1;
    }

    //文件打开成功 读写文件
    int middle = 0;
    while ((middle = fgetc(pf_read)) != EOF) {
        //读取成功  写文件
        fputc(middle, pf_write);
    }
    
    if (feof(pf_read)) {//在设置了与流关联的文件结尾指示符的情况下,将返回非零值。否则,将返回零
        printf("文件正常结束\n");
    }
    else if (ferror(pf_read)) {//在设置了与流关联的错误指示器的情况下,将返回非零值。   否则,将返回零。
        printf("文件读取失败结束\n");
    }

    //关闭文件
    fclose(pf_read);
    pf_read = NULL;
    fclose(pf_write);
    pf_write = NULL;

    return 0;
}

//atoi 和模拟实现atoi(字符串转化为整数)

#include<stdio.h>
//#include<stdlib.h>//atoi头文件
#include<assert.h>
#include<ctype.h>//  isspace  isdigit头文件
#include<limits.h>//INT_MAX  INT_MIN头文件

enum is_legitimate
{
    NOOK,//0  非法
    OK//1     合法
};

enum is_legitimate number;//记录my_atoi返回值是否合法

int my_atoi(const char* point) {

    int flag = 1;

    //1.传的空指针
    /*assert(point != NULL);*/
    if (point == NULL) {
        return 0;
    }

    //2.空字符串
    if (*point == '\0') {
        return 0;
    }

    //3.遇到空白字符
    while (isspace(*point)) {
        point++;
    }

    //4.正负号问题
    if (*point == '+') {
        flag = 1;
        point++;
    }
    else if(*point=='-') {
        flag = -1;
        point++;
    }

    //5.处理数字字符
    long long number1 = 0;
    while (isdigit(*point)) {
        number1 = number1 * 10 + flag*(*point - '0');
        if (number1 > INT_MAX || number1 < INT_MIN) {//预防超出范围
            return 0;
        }
        point++;
    }

    if (*point == '\0') {
        number = OK;
        return (int)number1;
    }
    else {
        //非数字字符
        return 0;
    }

}
int main()
{
    const char* point = "  120";
    int middle = my_atoi(point);
    if (number == OK) {
        printf("正常的转换:%d\n", middle);
    }
    else {
        printf("非法转换\n");
    }
    
    return 0;
}

//找出数组只出现一次的数字

#include<stdio.h>
void Find(int array[], int size, int* number1, int* number2)
{

    //1.把所有数据异或
    int frequency = 0;
    int middle = 0;
    for (frequency = 0; frequency < size; frequency++) {
        middle ^= array[frequency];
    }

    //2.计算middle的哪一位为1
    int find = 0;
    for (frequency < 0; frequency < 32; frequency++)
    {
        if (((middle >> frequency) & 1) == 1) {
            find = frequency;
            break;
        }
    }

    //3.在find位为1的放在一个组,为0的放在一个组
    int one = 0;
    int two = 0;
    for (frequency = 0; frequency < size; frequency++) {
        if (((array[frequency] >> find) & 1) == 1) {
            one ^= array[frequency];
        }
        else {
            two ^= array[frequency];
        }
    }
    *number1 = one;
    *number2 = two;
}
int main()
{
    int array[] = { 1, 2, 3, 4, 5, 6, 1, 2, 3, 4 };
    int size = sizeof(array) / sizeof(array[0]);
    int number1 = 0;
    int number2 = 0;
    Find(array, size, &number1, &number2);
    printf("%d %d\n", number1, number2);
    return 0;
}

/*--------------------------预处理(最后的倔强)--------------------------------

    1.   .c文件到.exe文件(一个.c文件通过编译器转换为.obj文件,然后通过链接器(链接库,也可以说成静态库))弄成可执行程序,通过运行环境,获取运行结果

    2.     翻译环境
           
           编译器分为预处理,编译和汇编三个成分
           预处理(预编译):第一完成头文件的包含;
                  第二#define定义的宏和符号的替换;
                  第三注释删除;
                  这些都是文本操作
           编译:把C语言代码转换为汇编代码(1.语法分析
                                         2.词义分析
                                         3.语义分析
                                         4.符号汇总)
           汇编:将汇编代码转换成了二进制指令(机器指令) 生成了符号表

           链接器:把多个目标的头文件进行链接(1.合并段表
                                            2.符号表的合并和重定位)
            
            运行坏境1.程序必须载入内存中,一般由操作系统来完成。在独立坏境中,程序的载入必须手工完成或通过可执行代码完成
                    2.程序执行开始从main函数开始
                    3.系统分配内存空间
                    4.终止程序(正常从main终止或意外终止)
*/

//1.预定义符号

#include<stdio.h>
int main()
{
    //printf("%s\n", __FILE__);//进行编辑的源文件
    //printf("%d\n", __LINE__);//文件当前行号
    //printf("%s\n", __DATE__);//文件被编译的日期
    //printf("%s\n", __TIME__);//文件被编译的时间
    //printf("%s\n", __FUNCTION__);//文件被编译的函数名
    
    //正确用法--写日记
    FILE* pf = fopen("test.txt", "a+");//追加数据
    if (pf == NULL) {
        perror("fopen\n");
        return 1;
    }

    int frequency = 0;
    for (frequency = 0; frequency < 10; frequency++) {
        fprintf(pf, "%d : %s %d %s %s \n", frequency, __FILE__, __LINE__, __DATE__, __TIME__);
    }

    fclose(pf);
    pf = NULL;

    return 0;
}

//2.#define定义标识符骚操作  

#include<stdio.h>
#define CASE break;case
#define NUMBER 100  
int main()
{
    
    int frequency = 0;
    switch (scanf("%d", &frequency)) {
    case1:
        CASE 2 :
            CASE 3 :
            break;

    }
#undef NUMBER//移除宏定义
    //printf("%d\n", NUMBER);//出错
    return 0;
}

//3. #(替换变量名为字符串)和##(将两个符号链接在一起)

#include<stdio.h>
#define PRINTF(accept) printf("this letter "#accept" value is%d\n",accept);
#define LINK(one,two) one##two
int main()
{
    //printf("1.helloworld\n");
    //printf("2.hello""world\n");//1. 和 2.打印效果相同 

    int number1 = 10;
    打印  this letter number1  value is  10
    //PRINTF(number1);//  #accept 表示"number1"

    printf("%d\n", LINK(numbe, r1));
    return 0;
}

/*5.宏和函数区别(宏有的地方和函数和相似  C语言也没有区分两者  我们习惯宏全部大写,函数不要全部大写)

   //优点:1.简单的运算中 ; 宏比函数在程序的规模和速度更胜一筹
           2.函数参数是固定类型  ;而宏的参数和类型无关

   //缺点:1.每次使用宏,一部分宏代码就会插入到程序中,增加了程序的长度;而函数代码只出现一个地方,每次调用会调用那个地方同一份代码
           2.宏不能调试
           3.宏由于类型无关,也会不够严谨,可能还会有优先级的问题
           4.宏不可以递归(宏只做简单的文本替换,且只替换一次,如果出现递归定义,就会无法被完全替换,导致后续编译时原宏名被当作函数)  而函数可以

*/

//6.条件编译(可以嵌套)  调试型代码,删了可惜,不删碍事,所以我们可以选择性编译   

#include<stdio.h>
#define NUMBER 
int main()
{

//#if 1-2 //if  常量表达式
//    printf("有这个数字");
//#endif

    //如果NUMBER定义了,下面参与编译
//#ifdef NUMBER
//    printf("有这个数字\n");
//#endif
//
//#if defined NUMBER
//    printf("有这个数字\n");
//#endif


    //如果NUMBER没定义,下面参与编译
#ifndef NUMBER
    printf("这个数字没定义\n");
#endif

#if !defined NUMBER
    printf("这个数字没定义\n");
#endif

#if 1==2
    printf("第一个执行\n");//第一个为真  第二个即使为真也不执行
#elif 2==3
    printf("第二个执行\n");
#else
    printf("第三个执行\n");
#endif
    return 0;
}

//7.文件包含
/*    
      " " 和 < >包含头文件的本质区别是:查找策略不同  
      " " 1.先从自己代码所在的目录查找 2.如果1找不到,则在库函数的头文件目录查找
      < > 直接去库函数头文件所在的目录下查找

      嵌套文件包含   同一个头文件被重复多次包含(意味这代码被拷贝多份,代码冗余)  如何防止呢?
                    1. #pragma once
                    2. #ifndef __AddressBook_H__
                       #define __AddressBook_H__
                       #endif

#include<stdio.h>//库文件包含,C语言库中提供的函数   <>
#ifndef __AddressBook_H__
#define __AddressBook_H__
#include"stdio.h"//本地文件包含,自定义的函数使用  ""  //这样效率有点低了
int main()
{  
    printf("HelloWorld\n");
    return 0;
}
#endif

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
GeoPandas是一个开源的Python库,旨在简化地理空间数据的处理和分析。它结合了Pandas和Shapely的能力,为Python用户提供了一个强大而灵活的工具来处理地理空间数据。以下是关于GeoPandas的详细介绍: 一、GeoPandas的基本概念 1. 定义 GeoPandas是建立在Pandas和Shapely之上的一个Python库,用于处理和分析地理空间数据。 它扩展了Pandas的DataFrame和Series数据结构,允许在其中存储和操作地理空间几何图形。 2. 核心数据结构 GeoDataFrame:GeoPandas的核心数据结构,是Pandas DataFrame的扩展。它包含一个或多个列,其中至少一列是几何列(geometry column),用于存储地理空间几何图形(如点、线、多边形等)。 GeoSeries:GeoPandas中的另一个重要数据结构,类似于Pandas的Series,但用于存储几何图形序列。 二、GeoPandas的功能特性 1. 读取和写入多种地理空间数据格式 GeoPandas支持读取和写入多种常见的地理空间数据格式,包括Shapefile、GeoJSON、PostGIS、KML等。这使得用户可以轻松地从各种数据源中加载地理空间数据,并将处理后的数据保存为所需的格式。 2. 地理空间几何图形的创建、编辑和分析 GeoPandas允许用户创建、编辑和分析地理空间几何图形,包括点、线、多边形等。它提供了丰富的空间操作函数,如缓冲区分析、交集、并集、差集等,使得用户可以方便地进行地理空间数据分析。 3. 数据可视化 GeoPandas内置了数据可视化功能,可以绘制地理空间数据的地图。用户可以使用matplotlib等库来进一步定制地图的样式和布局。 4. 空间连接和空间索引 GeoPandas支持空间连接操作,可以将两个GeoDataFrame按照空间关系(如相交、包含等)进行连接。此外,它还支持空间索引,可以提高地理空间数据查询的效率。
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小手冰凉ya

你的鼓励是我最大动力谢谢你

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值