C语言数据库管理系统示例:文件操作、内存管理、错误处理与动态数据库设计 栈和堆的内存分配

embedded/2024/12/21 20:37:18/

C语言的管理数据库完整的小型系统示例:

#include <stdio.h>      // 引入标准输入输出库,提供printf等功能
#include <assert.h>      // 引入断言库,用于调试时检查条件
#include <stdlib.h>      // 引入标准库,提供malloc、free、exit等功能
#include <errno.h>       // 引入错误号库,用于获取系统调用的错误号
#include <string.h>      // 引入字符串处理库,提供strncpy等字符串操作函数#define MAX_DATA 512     // 定义常量MAX_DATA为512,用于指定名字和邮件的最大长度
#define MAX_ROWS 100     // 定义常量MAX_ROWS为100,表示数据库最多可以有100条记录// 地址结构体,表示每条记录
struct Address {int id;              // 记录的唯一标识符int set;             // 标记记录是否已被设置,0表示未设置,1表示已设置char name[MAX_DATA]; // 存储联系人姓名的数组,最大长度为MAX_DATAchar email[MAX_DATA];// 存储联系人电子邮件的数组,最大长度为MAX_DATA
};// 数据库结构体,表示整个数据库
struct Database {struct Address rows[MAX_ROWS];  // 数组存储每一条记录,最多MAX_ROWS条记录
};// 连接结构体,用于表示数据库连接,包括文件和数据库内存数据
struct Connection {FILE *file;             // 文件指针,指向数据库文件struct Database *db;    // 指向内存中数据库数据的指针
};// 错误处理函数,打印错误信息并终止程序
void die(const char *message)
{if (errno) {                 // 如果有错误号perror(message);         // 打印系统错误信息} else {printf("ERROR: %s\n", message);  // 否则打印自定义的错误消息}exit(1);  // 退出程序
}// 打印地址(联系人的)信息
void Address_print(struct Address *addr)
{printf("%d %s %s\n", addr->id, addr->name, addr->email);  // 打印id、name和email
}// 从文件中加载数据库
void Database_load(struct Connection *conn)
{int rc = fread(conn->db, sizeof(struct Database), 1, conn->file);  // 从文件读取数据库if (rc != 1) die("Failed to load database.");  // 如果读取失败,调用die函数输出错误信息并退出
}// 打开数据库文件,返回连接对象
struct Connection *Database_open(const char *filename, char mode)
{struct Connection *conn = malloc(sizeof(struct Connection));  // 为Connection结构体分配内存if (!conn) die("Memory error");  // 如果内存分配失败,调用die函数conn->db = malloc(sizeof(struct Database));  // 为Database结构体分配内存if (!conn->db) die("Memory error");  // 如果内存分配失败,调用die函数if (mode == 'c') {  // 如果是创建模式,打开文件进行写操作conn->file = fopen(filename, "w");} else {  // 如果是读取模式,打开文件进行读写操作conn->file = fopen(filename, "r+");if (conn->file) {  // 如果文件打开成功Database_load(conn);  // 从文件加载数据库}}if (!conn->file) die("Failed to open the file");  // 如果文件无法打开,调用die函数退出return conn;  // 返回连接对象
}// 关闭数据库连接并释放相关资源
void Database_close(struct Connection *conn)
{if (conn) {  // 如果连接对象不为空if (conn->file) fclose(conn->file);  // 关闭文件if (conn->db) free(conn->db);  // 释放数据库内存free(conn);  // 释放连接对象内存}
}// 将数据库内容写入文件
void Database_write(struct Connection *conn)
{rewind(conn->file);  // 将文件指针回到文件开头int rc = fwrite(conn->db, sizeof(struct Database), 1, conn->file);  // 将数据库内容写入文件if (rc != 1) die("Failed to write database.");  // 如果写入失败,调用die函数rc = fflush(conn->file);  // 刷新文件流,确保所有数据都写入文件if (rc == -1) die("Cannot flush database.");  // 如果刷新失败,调用die函数
}// 创建数据库,初始化每条记录
void Database_create(struct Connection *conn)
{int i = 0;for (i = 0; i < MAX_ROWS; i++) {struct Address addr = {.id = i, .set = 0};  // 初始化每条记录,id为i,set为0conn->db->rows[i] = addr;  // 将初始化的记录赋值给数据库的相应位置}
}// 设置数据库某条记录的信息
void Database_set(struct Connection *conn, int id, const char *name, const char *email)
{struct Address *addr = &conn->db->rows[id];  // 获取指定id的记录if (addr->set) die("Already set, delete it first");  // 如果记录已设置,则报错addr->set = 1;  // 设置记录标记为已设置// 复制名字到记录char *res = strncpy(addr->name, name, MAX_DATA);if (!res) die("Name copy failed");  // 如果复制失败,调用die函数// 复制电子邮件到记录res = strncpy(addr->email, email, MAX_DATA);if (!res) die("Email copy failed");  // 如果复制失败,调用die函数
}// 获取并打印指定id的记录
void Database_get(struct Connection *conn, int id)
{struct Address *addr = &conn->db->rows[id];  // 获取指定id的记录if (addr->set) {  // 如果记录已设置Address_print(addr);  // 打印记录} else {die("ID is not set");  // 如果记录未设置,则报错}
}// 删除指定id的记录
void Database_delete(struct Connection *conn, int id)
{struct Address addr = {.id = id, .set = 0};  // 初始化一个删除的记录,id为id,set为0conn->db->rows[id] = addr;  // 将该记录写入数据库
}// 列出所有已设置的记录
void Database_list(struct Connection *conn)
{int i = 0;struct Database *db = conn->db;for (i = 0; i < MAX_ROWS; i++) {struct Address *cur = &db->rows[i];  // 获取当前记录if (cur->set) {  // 如果记录已设置Address_print(cur);  // 打印记录}}
}// 主函数:根据命令行参数执行相应的数据库操作
int main(int argc, char *argv[])
{if (argc < 3) die("USAGE: ex17 <dbfile> <action> [action params]");  // 如果参数不足,报错并退出char *filename = argv[1];  // 获取数据库文件名char action = argv[2][0];  // 获取操作类型(c=create, g=get, s=set, d=del, l=list)struct Connection *conn = Database_open(filename, action);  // 打开数据库文件并返回连接对象int id = 0;if (argc > 3) id = atoi(argv[3]);  // 如果有id参数,转换为整数if (id >= MAX_ROWS) die("There's not that many records.");  // 如果id超过最大记录数,报错并退出switch (action) {case 'c':  // 如果操作类型是创建数据库Database_create(conn);  // 创建数据库Database_write(conn);   // 写入文件break;case 'g':  // 如果操作类型是获取记录if (argc != 4) die("Need an id to get");  // 如果缺少id参数,报错Database_get(conn, id);  // 获取并打印指定id的记录break;case 's':  // 如果操作类型是设置记录if (argc != 6) die("Need id, name, email to set");  // 如果缺少参数,报错Database_set(conn, id, argv[4], argv[5]);  // 设置指定id的记录Database_write(conn);  // 写入文件break;case 'd':  // 如果操作类型是删除记录if (argc != 4) die("Need id to delete");  // 如果缺少id参数,报错Database_delete(conn, id);  // 删除指定id的记录Database_write(conn);  // 写入文件break;case 'l':  // 如果操作类型是列出记录Database_list(conn);  // 列出所有已设置的记录break;default:  // 如果操作类型无效,报错die("Invalid action, only: c=create, g=get, s=set, d=del, l=list");}Database_close(conn);  // 关闭数据库连接并释放资源return 0;  // 返回0表示程序正常结束
}

对以上的文件操作库函数进行解析:

1.fopen 函数

FILE *fopen(const char *filename, const char *mode);

功能:fopen 是 C 标准库中用于打开文件的函数,用于指定文件的打开模式,并返回一个指向文件的指针,允许程序读写文件
参数:
        filename:要打开的文件名(包括路径,如果文件不在当前目录)。
        mode:打开文件的模式,指定如何访问文件。
返回值: 如果文件成功打开,返回指向文件的 FILE 指针,后续的文件操作都通过这个指针进行。 如果文件无法打开(例如文件不存在,权限不足等),返回 NULL,此时可以通过 errno 或 perror 获取错误信息。


2. freadfwrite 函数

int fread(void *ptr, size_t size, size_t count, FILE *stream);

功能:从指定的文件流 stream 中读取 count 个对象,每个对象大小为 size 字节,并将它们存储在 ptr 指向的内存区域中。
参数:
        ptr:指向存储读取数据的缓冲区的指针。
        size:每个对象的大小(以字节为单位)。
        count:要读取的对象数量。
        stream:指向 FILE 类型的文件指针,指定要读取的文件。
返回值:返回实际读取的对象数量。如果返回值小于 count,则表示发生了错误或文件结束。

int fwrite(const void *ptr, size_t size, size_t count, FILE *stream);

功能:向指定的文件流 stream 写入数据。写入 count 个对象,每个对象大小为 size 字节,数据来自 ptr 指向的内存区域。
参数:
        ptr:指向要写入数据的缓冲区的指针。
        size:每个对象的大小(以字节为单位)。
        count:要写入的对象数量。
        stream:指向 FILE 类型的文件指针,指定要写入的文件。
返回值:返回实际写入的对象数量。如果返回值小于 count,则表示发生了错误。

3. rewind 函数

void rewind(FILE *stream);

功能:将文件指针 stream 移动到文件的开头。
参数:
        stream:指向要操作的文件流。
返回值:没有返回值。成功时,文件指针指向文件的起始位置。如果发生错误,ferror 会返回一个非零值。

4. fflush 函数

int fflush(FILE *stream);

功能:刷新输出缓冲区,将缓冲区中的数据强制写入到文件中。
参数:
        stream:指向要刷新的文件流。如果 stream 为 NULL,则刷新所有输出流的缓冲区。
返回值: 如果成功,返回 0。 如果发生错误,返回 EOF,并设置 errno 来指示错误原因。

 C 标准库中用于处理错误的相关机制

1、errno

        errno 是一个全局变量,定义在 <errno.h> 头文件中,它用于指示上一个系统调用或库函数发生的错误类型。
        作用:每当某个函数(特别是系统调用或库函数)失败时,它会将一个错误代码存储在 errno 中。该错误代码是一个整数,代表具体的错误类型。
         errno 的值:它的值会随着错误发生而改变。因此,在调用可能设置 errno 的函数后,需要检查其值并根据错误代码提供相应的处理。
常见的 errno 错误代码:
        EINVAL:无效的参数。
        ENOENT:没有该文件或目录。
        ENOMEM:内存不足。
        EACCES:权限拒绝。
        EIO:输入输出错误。
        EPERM:操作不允许。
        EIO:硬件错误。

示例:

#include <stdio.h>
#include <errno.h>
#include <string.h>int main() {FILE *file = fopen("nonexistent_file.txt", "r");if (file == NULL) {// 打印错误代码printf("Error code: %d\n", errno);// 打印错误描述printf("Error description: %s\n", strerror(errno));}return 0;
}

2、perror

   perror 是 C 标准库中用于打印 errno 的错误信息的函数,它会根据 errno 中的错误代码输出相应的错误描述。它输出的信息通常包括用户自定义的前缀字符串(可选)和错误消息。
函数原型:

void perror(const char *s);

        参数 s:是用户提供的字符串,如果提供了,perror 会先输出该字符串,再输出对应的错误描述;如果不提供,直接输出错误描述。
        输出:perror 会输出一个标准的错误信息,格式为:<prefix>: <error_description>。

堆和栈的内存分配

        堆就是你电脑中的剩余内存,你可以通过malloc访问它来获取更多内存,OS会使用内部函数为你注册一块内存区域,并且返回指向它的指针。当你使用完这片区域时,你应该使用free把它交还给OS,使之能被其它程序复用。如果你不这样做就会导致程序“泄露”内存,但是Valgrind会帮你监测这些内存泄露。

        栈是一个特殊的内存区域,它储存了每个函数的创建的临时变量,它们对于该函数为局部变量。它的工作机制是,函数的每个函数都会“压入”栈中,并且可在函数内部使用。它是一个真正的栈数据结构,所以是后进先出的。这对于main中所有类似char sectionint id的局部变量也是相同的。使用栈的优点是,当函数退出时C编译器会从栈中“弹出”所有变量来清理。这非常简单,也防止了栈上变量的内存泄露。

        理清内存的最简单的方式是遵守这条原则:如果你的变量并不是从malloc中获取的,也不是从一个从malloc获取的函数中获取的,那么它在栈上。

        而除了堆和栈之外,还有数据段(Data Segment),用于存储程序中的全局变量、静态变量和常量数据,分为:已初始化数据段(存储已初始化的全局变量和静态变量)未初始化数据段(BSS段)存储未初始化的全局变量和静态变量,程序运行时会将它们初始化为 0。特点:数据段在程序加载时会被加载到内存中,并且生命周期与程序相同
 

附加题

  • die函数需要接收conn变量作为参数,以便执行清理并关闭它。
    void die(struct Connection *conn,const char *message)
    {if(errno) {perror(message);} else {printf("ERROR: %s\n", message);}Database_close(conn);exit(1);
    }
  • 修改代码,使其接收参数作为MAX_DATAMAX_ROWS,将它们储存在Database结构体中,并且将它们写到文件。这样就可以创建任意大小的数据库
     
    // 修改struct Database
    struct Database {int max_data;  // 动态存储最大数据大小int max_rows;  // 动态存储最大行数struct Address *rows;  // 动态分配存储的地址
    };// 修改struct Connection *Database_open函数
    struct Connection *Database_open(const char *filename, char mode, int max_data, int max_rows)
    {struct Connection *conn = malloc(sizeof(struct Connection));if (!conn) die(conn, "Memory error");conn->db = malloc(sizeof(struct Database));if (!conn->db) die(conn, "Memory error");// 存储MAX_DATA和MAX_ROWSconn->db->max_data = max_data;conn->db->max_rows = max_rows;// 动态分配数据库条目conn->db->rows = malloc(max_rows * sizeof(struct Address));if (!conn->db->rows) die(conn, "Memory error for rows");if (mode == 'c') {conn->file = fopen(filename, "w");} else {conn->file = fopen(filename, "r+");if (conn->file) {Database_load(conn);}}if (!conn->file) die(conn, "Failed to open the file");return conn;
    }// 修改主函数中输入参数格式if (argc < 5) die(NULL, "USAGE: ex17 <dbfile> <action> <max_data> <max_rows> [action params]");char *filename = argv[1];char action = argv[2][0];int max_data = atoi(argv[3]);int max_rows = atoi(argv[4]);struct Connection *conn = Database_open(filename, action, max_data, max_rows);int id = 0;if (argc > 5) id = atoi(argv[5]);if (id >= conn->db->max_rows) die(conn, "There's not that many records.");switch (action) {case 'c':Database_create(conn);Database_write(conn);break;case 'g':if (argc != 6) die(conn, "Need an id to get");Database_get(conn, id);break;case 's':if (argc != 7) die(conn, "Need id, name, email to set");Database_set(conn, id, argv[6], argv[7]);Database_write(conn);break;case 'd':if (argc != 6) die(conn, "Need id to delete");Database_delete(conn, id);Database_write(conn);break;case 'l':Database_list(conn);break;default:die(conn, "Invalid action, only: c=create, g=get, s=set, d=del, l=list");}
    
  • 数据库添加更多操作,比如find
     
    // 添加相应内容
    void Database_find(struct Connection *conn,const char *name)
    {struct Database *db = conn->db;for (int i = 0; i < db->max_rows; i++) {struct Address *cur = &db->rows[i];if (cur->set && strcmp(cur->name,name) == 0) {Address_print(db,i);}}}case 'f':if (argc != 7) die(conn, "Need an id to get");Database_find(conn,argv[6]);break;
    
  • 查询C如何打包结构体,并且试着弄清楚为什么你的文件是相应的大小。看看你是否可以计算出结构体添加一些字段之后的新大小。

    在 C 语言中,打包(packing)结构体是指调整结构体的内存布局,减少或消除由于字节对齐造成的内存填充(padding)。字节对齐的目的是提高处理器访问内存时的效率,尤其是在处理较大数据时。

    1. 结构体的内存布局(Padding)

    通常情况下,编译器会根据结构体中成员的类型对内存进行对齐。每个数据类型都有一个对齐要求(alignment requirement),例如:
    char 通常对齐到 1 字节边界(sizeof(char) = 1)
    int 通常对齐到 4 字节边界(sizeof(int) = 4)
    如果结构体成员没有按照其对齐要求对齐,编译器会自动插入填充字节(padding)来确保每个成员按照适当的对齐要求存储。
    例如:
    #include <stdio.h>struct MyStruct {char a;int b;short c;
    };int main() {printf("Size of MyStruct: %lu\n", sizeof(struct MyStruct));return 0;
    }// 输出结果为: Size of MyStruct: 12
    
    布局如下:
    | a(char) | padding(3 bytes) | b(int) | c(short) | padding(2 bytes) |
    
    2. 结构体打包(packing)
    为了打包结构体(消除内存中的填充字节),我们可以使用编译器特性,例如 #pragma pack 指令(具体依赖于编译器)或 __attribute__((packed))
    例如:
    #include <stdio.h>#pragma pack(1)  // 设置结构体按 1 字节对齐struct MyStruct {char a;int b;short c;
    };int main() {printf("Size of MyStruct: %lu\n", sizeof(struct MyStruct));return 0;
    }// 输出结果:Size of MyStruct: 7
    
  • Address添加一些字段,使它们可被搜索。

    类似find功能
     
  • 编写一个shell脚本来通过以正确顺序运行命令执行自动化测试。提示:在bash顶端使用使用set -e,使之在任何命令发生错误时退出。
     
    #!/bin/bash# 启用错误退出模式
    #set -e# 定义文件和路径
    C_SOURCE_FILE="ex17.c"
    C_EXEC_FILE="ex17"
    TEST_DB_FILE="testdb.dat"# 编译 C 代码
    echo "Compiling the C code..."
    gcc -Wall -g $C_SOURCE_FILE -o $C_EXEC_FILE# 测试 1: 创建数据库
    echo "Running test 1: Creating a new database..."
    ./$C_EXEC_FILE $TEST_DB_FILE c# 测试 2: 设置数据
    echo "Running test 2: Setting data for ID 0..."
    ./$C_EXEC_FILE $TEST_DB_FILE s 0 "Joe Alex" "joe@example.com"# 测试 3: 获取数据
    echo "Running test 3: Getting data for ID 0..."
    ./$C_EXEC_FILE $TEST_DB_FILE g 0# 测试 4: 列出所有数据
    echo "Running test 4: Listing all entries..."
    ./$C_EXEC_FILE $TEST_DB_FILE l# 测试 5: 删除数据
    echo "Running test 5: Deleting data for ID 0..."
    ./$C_EXEC_FILE $TEST_DB_FILE d 0# 测试 6: 确认删除
    echo "Running test 6: Verifying data for ID 0..."
    ./$C_EXEC_FILE $TEST_DB_FILE g 0# 清理测试文件
    echo "Cleaning up after tests..."
    rm  $TEST_DB_FILEecho "All tests passed successfully!"
    
  • 尝试重构程序,使用单一的全局变量来储存数据库连接。这个新版本和旧版本比起来如何?

    使用全局变量,在重构后,struct Connection *conn 被定义为一个全局变量。所有与数据库相关的操作都直接访问这个全局变量,无需再通过函数参数传递连接。函数 Database_open、Database_close、Database_write、Database_create 等不再需要接受 conn 作为参数,因为全局变量 conn 可以直接访问。
    新版本与旧版本的对比:
    优点:
    简洁性: 代码中函数签名变得更简洁,不再需要通过参数传递 conn,而是直接使用全局变量。
    易于访问: 在多个函数中直接访问全局变量,避免了多次传递参数的麻烦。

    缺点:
    可维护性差: 使用全局变量会让程序的状态管理变得更复杂,特别是在多线程或多任务环境中。函数内部无法独立操作数据库连接,而是依赖于全局状态。
    难以测试: 单一的全局变量增加了测试的复杂性。测试时,可能需要手动初始化和清理全局变量,尤其是在多个测试用例之间共享状态时。
    灵活性降低: 使用全局变量意味着函数之间没有明确的数据流动关系,导致代码的解耦性降低,后期的扩展可能变得更加困难。

    如果程序的规模较小,且没有复杂的模块化需求,使用全局变量可以简化代码,减少函数参数的传递。 但在大型项目或需要高可维护性和高可测试性的项目中,尽量避免使用全局变量,或者将其限制在必要的范围内。
     
  • 搜索“栈数据结构”,并且在你最喜欢的语言中实现它,然后尝试在C中实现。

    使用python实现栈:
    class Stack:def __init__(self):# 使用列表来存储栈中的元素self.items = []def is_empty(self):# 判断栈是否为空return len(self.items) == 0def push(self, item):# 将元素推入栈中self.items.append(item)def pop(self):# 从栈中弹出一个元素,并返回该元素if not self.is_empty():return self.items.pop()else:raise IndexError("pop from empty stack")def peek(self):# 查看栈顶的元素,不移除它if not self.is_empty():return self.items[-1]else:raise IndexError("peek from empty stack")def size(self):# 返回栈中元素的个数return len(self.items)# 示例用法
    if __name__ == "__main__":stack = Stack()stack.push(1)stack.push(2)stack.push(3)print(f"栈顶元素: {stack.peek()}")  # 输出 3print(f"栈的大小: {stack.size()}")  # 输出 3print(f"弹出的元素: {stack.pop()}")  # 输出 3print(f"栈的大小: {stack.size()}")  # 输出 2
    

    使用C语言实现栈:

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>#define MAX_SIZE 100  // 栈的最大容量// 定义栈结构体
    typedef struct {int items[MAX_SIZE];  // 存储栈元素的数组int top;  // 栈顶指针
    } Stack;// 初始化栈
    void init_stack(Stack *s) {s->top = -1;  // 初始化时栈为空
    }// 判断栈是否为空
    bool is_empty(Stack *s) {return s->top == -1;
    }// 判断栈是否已满
    bool is_full(Stack *s) {return s->top == MAX_SIZE - 1;
    }// 将元素推入栈中
    void push(Stack *s, int item) {if (is_full(s)) {printf("栈满,无法推入元素\n");return;}s->items[++(s->top)] = item;
    }// 从栈中弹出一个元素
    int pop(Stack *s) {if (is_empty(s)) {printf("栈空,无法弹出元素\n");exit(1);  // 异常退出}return s->items[(s->top)--];
    }// 查看栈顶元素
    int peek(Stack *s) {if (is_empty(s)) {printf("栈空,无法查看栈顶元素\n");exit(1);  // 异常退出}return s->items[s->top];
    }// 返回栈的大小
    int size(Stack *s) {return s->top + 1;
    }int main() {Stack stack;init_stack(&stack);push(&stack, 1);push(&stack, 2);push(&stack, 3);printf("栈顶元素: %d\n", peek(&stack));  // 输出 3printf("栈的大小: %d\n", size(&stack));  // 输出 3printf("弹出的元素: %d\n", pop(&stack));  // 输出 3printf("栈的大小: %d\n", size(&stack));  // 输出 2return 0;
    }
    

    Python和C实现对比
    在Python中,栈实现依赖于列表(list),Python自动处理内存管理。栈的大小可以动态变化。 在C中,我们必须手动管理内存(栈大小),并且栈的最大容量是预定义的(MAX_SIZE)。如果需要动态调整大小,需要重新分配内存。  Python的代码更加简洁和灵活,因为它具有自动内存管理和内建的动态数据结构(如list)。 C需要更多的手动内存管理和对边界条件的检查,代码相对繁琐。


http://www.ppmy.cn/embedded/147621.html

相关文章

提升PHP技能:18个实用高级特性

掌握PHP基础知识只是第一步。 深入了解这18个强大的PHP特性&#xff0c;将显著提升您的开发效率和代码质量。 1、超越 __construct() 的魔法方法 虽然 __construct() 为大多数开发者所熟知&#xff0c;PHP 却提供了更多强大的魔术方法&#xff0c;例如&#xff1a; class Da…

Transformer 中 Self-Attention 的二次方复杂度(Quadratic Complexity )问题及改进方法:中英双语

Transformer 中 Self-Attention 的二次方复杂度问题及改进方法 随着大型语言模型&#xff08;LLM&#xff09;输入序列长度的增加&#xff0c;Transformer 结构中的核心模块——自注意力机制&#xff08;Self-Attention&#xff09; 的计算复杂度和内存消耗都呈现二次方增长。…

基于xss-lab的绕过

绕过&#xff1a;闭合 "><script>alert(1)</script>< 11.2. 实体化绕过&#xff08;使用不被实体化的字符构造payload&#xff09; 使用了htmlspecialchars()函数&#xff0c;实体化一些字符&#xff0c;但默认配置不过滤单引号&#xff0c;构造单引号…

每天学习一个思维模型 - 损失规避

定义 损失规避&#xff08;Loss aversion&#xff09;&#xff0c;又称损失厌恶&#xff0c;指人们面对同样数量的利益和损失时&#xff0c;认为损失更加令他们难以忍受。损失带来的负效用为收益正效用的2至2.5倍。损失厌恶反映了人们的风险偏好并不是一致的&#xff0c;当涉及…

联邦学习防止数据泄露

文章目录 联邦学习防止数据泄露的原理联邦学习的优势联邦学习与集中式学习的成本分析联邦学习的实际应用案例个人设想参考文献 联邦学习 (Federated Learning) 是一种分布式机器学习技术&#xff0c;旨在解决数据隐私保护问题。它允许在分散的数据源上进行模型训练&#xff0c;…

目标检测任务中根据真实坐标和预测坐标计算IOU

本文记录了在目标检测任务中根据目标的真实坐标和预测坐标计算 iou 交并比指标的代码。 文章目录 一、代码 一、代码 import numpy as np import matplotlib.pyplot as plt import matplotlib.image as mpimgdef calculate_iou(real_label, predicted_label, img_width, img_h…

Docker dockerfile镜像编码 centos7

一、 大多数docker基础镜像使用locale查看编码&#xff0c;发现默认编码都是POSIX&#xff0c;这会导致中文乱码。 解决方法如下: 二、首先使用locale -a查看容器所有语言环境 三、dockerfile中加入以下参数重新生成镜像   ENV LANGen_US.UTF-8   ENV TZAsia/Shanghai  …

react 项目打包二级目 使用BrowserRouter 解决页面刷新404 找不到路由

使用BrowserRouter package 配置 &#xff08;这部分代码可以不做配置也能实现&#xff09; {"homepage": "/admin",}vite.config 配置 export default defineConfig({base: /admin])BrowserRouter 添加配置项 <BrowserRouter basename/admin>&l…