笨方法学 C —— 练习17

附加题题目要求

  • die函数需要接收conn变量作为参数,以便执行清理并关闭它。
  • 修改代码,使其接收参数作为MAX_DATA和MAX_ROWS,将它们储存在Database结构体中,并且将它们写到文件。这样就可以创建任意大小的数据库。
  • 向数据库添加更多操作,比如find。
  • 查询C如何打包结构体,并且试着弄清楚为什么你的文件是相应的大小。看看你是否可以计算出结构体添加一些字段之后的新大小。
  • 向Address添加一些字段,使它们可被搜索。
  • 编写一个shell脚本来通过以正确顺序运行命令执行自动化测试。提示:在bash顶端使用使用set -e,使之在任何命令发生错误时退出。 译者注:使用Python编写多行脚本或许更方便一些。
  • 尝试重构程序,使用单一的全局变量来储存数据库连接。这个新版本和旧版本比起来如何?
全部代码
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

int MAX_DATA,MAX_ROWS;
struct Address {
    int id;
    int set;
    // char name[MAX_DATA];
    // char email[MAX_DATA];
    char *name,*email;
};

struct Database {
    int MAX_data;
    int MAX_rows;
    // struct Address rows[MAX_ROWS];
    struct Address *rows;
};

struct Connection {
    FILE *file;
    struct Database *db;
};

void die(const char *message, struct Connection *conn)
{
    if(errno) {
        perror(message);
    } else {
        printf("ERROR: %s\n", message);
    }
    if (conn) {
        Database_close(conn);
    }
    exit(1);
}

void Address_print(struct Address *addr)
{
    printf("%d %s %s\n",
            addr->id, addr->name, addr->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.", conn);
}

struct Connection *Database_open(const char *filename, char mode)
{
    struct Connection *conn = malloc(sizeof(struct Connection));
    if(!conn) die("Memory error", NULL);

    conn->db = malloc(sizeof(struct Database));
    if(!conn->db) die("Memory error", conn);

    

    if(mode == 'c') {
        conn->file = fopen(filename, "w");
        if (!conn->file) die("Cannot create the file", conn);
    } else {
        conn->file = fopen(filename, "r+");
        if (conn->file) {
            Database_load(conn);
        } else {
            die("Cannot open the file", conn);
        }
    }

    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.", conn);

    rc = fflush(conn->file);
    if(rc == -1) die("Cannot flush database.", conn);
}

void Database_create(struct Connection *conn, int MAX_DATA, int MAX_ROWS)
{
    int i = 0;
    conn->db->MAX_data = MAX_DATA;
    conn->db->MAX_rows = MAX_ROWS;
    conn->db->rows = (struct Address*)malloc(MAX_ROWS * sizeof(struct Address));
    for(i = 0; i < MAX_ROWS; i++) {
        // make a prototype to initialize it
        struct Address addr;
        addr.name = (char*)malloc(MAX_DATA*sizeof(char));
        addr.email = (char*)malloc(MAX_DATA*sizeof(char));
        // addr = {.id = i, .set = 0};
        addr.id = i;
        addr.set = 0;
        // then just assign it
        conn->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];
    if(addr->set) die("Already set, delete it first", conn);

    addr->set = 1;
    // WARNING: bug, read the "How To Break It" and fix this
    char *res = strncpy(addr->name, name, MAX_DATA);
    // demonstrate the strncpy bug
    if(!res) die("Name copy failed", conn);

    res = strncpy(addr->email, email, MAX_DATA);
    if(!res) die("Email copy failed", conn);
}

void Database_get(struct Connection *conn, int id)
{
    struct Address *addr = &conn->db->rows[id];

    if(addr->set) {
        Address_print(addr);
    } else {
        die("ID is not set", conn);
    }
}

void Database_delete(struct Connection *conn, int id)
{
    struct Address addr = {.id = id, .set = 0};
    conn->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);
        }
    }
}

void Database_find_name(struct Connection *conn,char *t_name)
{
    int flag = 0;
    for(int i = 0; i < conn->db->MAX_rows; i++) {
        // make a prototype to initialize it
        if (conn->db->rows[i].name == t_name)
        {
            flag = 1;
            Address_print(&(conn->db->rows[i]));
        }
    }
    if (!flag) die("Not find the Address of the name you mentioned",conn);
}

int main(int argc, char *argv[])
{
    if(argc < 4) die("USAGE: ex17 <dbfile> <action> [action params]", NULL);

    char *filename = argv[1];
    char action = argv[2][0];
    // MAX_DATA = atoi(argv[argc - 2]);
    // MAX_ROWS = atoi(argv[argc - 1]);
    struct Connection *conn = Database_open(filename, action);
    int id = 0;

    if(argc >= 4) id = atoi(argv[3]);
    if(id >= MAX_ROWS) die("There's not that many records.", conn);

    switch(action) {
        case 'c':
            if(argc != 5) die("Need MAX_DATA and MAX_ROWS to creat", conn);
            MAX_DATA = atoi(argv[argc - 2]);
            MAX_ROWS = atoi(argv[argc - 1]);
            Database_create(conn, MAX_DATA, MAX_ROWS);
            Database_write(conn);
            break;

        case 'f':
            if(argc != 4) die("Need a name to find",conn);
            char *t_name = argv[3];
            Database_find_name(conn,t_name);
            break;

        case 'g':
            if(argc != 4) die("Need an id to get", conn);

            Database_get(conn, id);
            break;

        case 's':
            if(argc != 6) die("Need id, name, email to set", conn);

            Database_set(conn, id, argv[4], argv[5]);
            Database_write(conn);
            break;

        case 'd':
            if(argc != 4) die("Need id to delete", conn);

            Database_delete(conn, 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", conn);
    }
    Database_close(conn);

    return 0;
}
  • 13
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

疯狂的码泰君

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值