C语言图书管理借阅系统——ncurses库的使用

Posted 杨博东的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言图书管理借阅系统——ncurses库的使用相关的知识,希望对你有一定的参考价值。

一、前言

作为一只大四狗,最近还跟着大二同学修了一门课(当然不是之前没通过啦),课程是高级语言课程设计,高级语言指的是C语言 :),内容是做一个XX管理系统,我选择了图书管理系统,先介绍下我做的系统:

  • 主要功能:
    • 读者信息管理:添加、修改、删除、查询读者信息。
    • 图书信息管理:添加图书、修改图书、删除图书、查询图书
    • 图书借阅归还:图书借阅和归还,以及列出借阅情况。
    • 信息统计汇总:图书总量统计、图书借阅统计等。
  • 日志功能:记录用户、图书、借阅相关信息的日志。
  • 参与对象:管理员和用户,管理员主要指图书馆相关工作负责人员,用户指老师或者教工,可以从图书馆借书。
  • 数据存储格式:文件。
  • 数据组织方式:链表。
  • 界面:ncurses库。
  • 其它:CMake组织项目、GitHub版本控制:代码地址 、Linux操作系统运行。

为了体现我大学四年也不是白念的,当然得体现出逼格,那就从界面下手,于是我选择了ncurses这个终端字符库,最后的界面是这样的:

整个界面分为三部分,上面显示系统名称和时间,用户登录之后还会显示用户名;左下是整个程序的功能菜单部分;右下是系统日志,负责动态显示添加用户、添加图书、借阅书籍等操作;三个部分由三个线程负责,但是由于ncurses库本身不是线程安全的(多个线程同时操作输出会出现问题),因此需要用互斥锁来互斥控制输出流程,后边会详细说。

二、ncueses库的使用

(1)介绍
简而言之就是一个终端下使用的,可以让你改变字符输出位置输出颜色并且创建窗口的图形库,更多介绍参见这里
(2)安装

$ sudo yum install ncurses-devel   //RedHat
$ sudo apt-get install libncurses5-dev libncursesw5-dev  //Ubuntu

(3)使用
有关它的使用,参考下面两篇文章和作者 非常详细 的代码示例,基本上就可以上手了:

文章一:NCURSES 函数简要参考:讲解ncurses的初始化、输入输出、颜色、窗口等等。
文章二:NCURSES Programming HOWTO中文版:作者有许多小例子。
参考三:代码示例

三、遇到的问题

(1)ncurses图形库不是线程安全的,之前设计的界面如下所示:

但是发现三个线程同时输出之后乱码,最后这个问题在stackoverflow上得到了明确: https://stackoverflow.com/questions/47878870/when-use-ncurses-in-multi-threaded-the-terminal-garbled,确实是由于线程安全问题引起的,最后加互斥锁解决,简易代码如下:

#include <stdio.h>
#include <ncurses.h>
#include <pthread.h>
#include <time.h>
#include <unistd.h>

pthread_mutex_t MUTEX;  /* mutex for sync display */

#define LOCK pthread_mutex_lock(&MUTEX)
#define UNLOCK pthread_mutex_unlock(&MUTEX)

typedef struct _WIN_struct 
    int startx, starty;
    int height, width;
 WIN;

WIN winTitle;      /* title win */
WIN winMenu;   /* Main menu win */
WIN winNews;       /* win news */

WINDOW *create_newwin(int height, int width, int starty, int startx) 
    WINDOW *local_win;
    local_win = newwin(height, width, starty, startx);
    box(local_win, 0, 0);
    wrefresh(local_win);
    return local_win;


char *getTimeNow() 
    time_t rawtime;
    struct tm *timeinfo;
    time(&rawtime);
    timeinfo = localtime(&rawtime);
    return asctime(timeinfo);


void *threadfunc_title(void *p) 
    WINDOW *windowTitle;
    LOCK;
    windowTitle = create_newwin(winTitle.height, winTitle.width, winTitle.starty, winTitle.startx);
    UNLOCK;

    /* show title and time */
    for (;;) 
        sleep(1);
    



void *threadfunc_menu(void *p) 
    WINDOW *windowMenu;
    LOCK;
    windowMenu = create_newwin(winMenu.height, winMenu.width, winMenu.starty, winMenu.startx);
    UNLOCK;

    for (;;) 
        /* now do nothing */
        sleep(1);
    



void *threadfunc_news(void *p) 
    WINDOW *windowNews;
    LOCK;
    windowNews = create_newwin(winNews.height, winNews.width, winNews.starty, winNews.startx);
    UNLOCK;

    for (;;) 
        sleep(1);
    


void initWin(WIN *p_win, int height, int width, int starty, int startx) 
    p_win->height = height;
    p_win->width = width;
    p_win->starty = starty;
    p_win->startx = startx;



int main(int argc, char *argv[])

    pthread_t pidTitle;
    pthread_t pidMenu;
    pthread_t pidNews;

    initscr();
    start_color();
    cbreak();
    keypad(stdscr, TRUE);
    noecho();

    /* init location */
    initWin(&winTitle, LINES*0.25, COLS, 0 , 0);
    initWin(&winMenu, LINES*0.75, COLS*0.60, LINES*0.25, 0);
    initWin(&winNews, LINES*0.75, COLS*0.40, LINES*0.25, COLS*0.60);

    pthread_create(&pidTitle, NULL, threadfunc_title, NULL);
    pthread_create(&pidMenu, NULL, threadfunc_menu, NULL);
    pthread_create(&pidNews, NULL, threadfunc_news, NULL);

    pthread_join(pidTitle, NULL);
    pthread_join(pidMenu, NULL);
    pthread_join(pidNews, NULL);

    endwin();
    return 0;

(2)while (!feof(fp)) 来判断文件结尾是不可靠的,feof(fp)判断文件结尾指的是:文件最后一个字符的下一个字符,具体参考 https://baike.baidu.com/item/feof/10942186?fr=aladdin ,使用 fread的弊端是它的返回值不能区分是到了文件结尾还是遇到了错误:

这是之前的代码:

int readUser() 
    FILE *fp;
    user *p = (user *) malloc(sizeof(user));
    user *q = (user *) malloc(sizeof(user));
    USER_HEAD = p;
    USER_MAXID = -1;

    fp = fopen(USER_PATH, "r+");
    if (fp == NULL) 
        fp = fopen(USER_PATH, "w+");
        free(q);
        return 0;
    

    while (!feof(fp)) 
        fscanf(fp, "%d %s %s %s %s %d\\n", &(q->user_id), q->user_stid,
               q->user_name, q->user_address, q->user_mail, &(q->user_status));

        /* update USER_MAXID*/
        if (q->user_id > USER_MAXID) 
            USER_MAXID = q->user_id;
        

        p->next = q;
        p = q;
        q = (user *) malloc(sizeof(user));
    

    p->next = NULL;
    free(q);
    fclose(fp);
    return 0;

最后使用read读取,加O_CREATE也可以防止文件不存在:

int readUser() 
    int fd;
    int size;
    user *p = (user *) malloc(sizeof(user));
    user *q = (user *) malloc(sizeof(user));
    USER_HEAD = p;
    USER_MAXID = 0;

    fd = open(USER_PATH, O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR);
    if (fd == -1) 
        perror("open");
        exit(1);
    

    while ((size = read(fd, q, sizeof(user))) != 0) 
        if (q->user_id > USER_MAXID) 
            USER_MAXID = q->user_id;
        
        p->next = q;
        p = q;
        q = (user *) malloc(sizeof(user));
    

    p->next = NULL;
    free(q);
    close(fd);
    return 0;

(3)c语言如何获取系统时间:使用localtime()和asctime()返回的字符串中会包含\\n,不太友好,这可能是历史原因,具体参考:http://www.developerq.com/article/1494738998,自己封装一个去掉\\n字符,代码如下:

char *getTimeNow() 
    char *timestr;
    time_t rawtime;
    struct tm *timeinfo;
    time(&rawtime);
    timeinfo = localtime(&rawtime);
    timestr = asctime(timeinfo);
    /* del \\n */
    timestr[strlen(timestr) - 1] = '\\0';
    return timestr;

[完]

以上是关于C语言图书管理借阅系统——ncurses库的使用的主要内容,如果未能解决你的问题,请参考以下文章

数据库系统原理课程设计 -----图书借阅管理系统

c语言程序设计图书馆源代码

c语言 数据结构课程设计 图书管理系统

图书管理系统

借同学百度知道问的,求一个C语言课程设计,关于图书馆借阅的程序。

求图书馆管理系统程序(c语言)