一、前言

作为一只大四狗,最近还跟着大二同学修了一门课(当然不是之前没通过啦),课程是高级语言课程设计,高级语言指的是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库的使用的更多相关文章

  1. 实例:建立图书借阅系统的UML模型

    1.需求分析 图书借阅系统的组成 2.具体的功能详细描述: (1)管理员登录系统,进入借书工作状态,等待借书处理. (2)读者找到所需图书,在借书处上刷卡机上刷卡. (3)管理员对借阅证进行资格审查. ...

  2. Linux系统下安装ncurses库

    ncurses库是一个Linux系统下的图形支持的函数库,字符终端处理库,包括面板和菜单. 今天在安装ncurses库的时候遇到了一些问题,现将遇到的问题所叙如下: 首先说明:本次安装采用的是源码包的 ...

  3. 个人项目开源之Django图书借阅系统源代码

    Django写的模拟图书借阅系统源代码已开源到码云 源代码

  4. 基于web的图书管理系统设计与实现

    原文链接:基于web的图书管理系统设计与实现 系统演示链接:点击这里查看演示 01 系统简述     图书管理系统就是利用计算机,结合互联网对图书进行结构化.自动化管理的一种软件,来提高对图书的管理效 ...

  5. 基于web的图书管理系统设计与实现(附演示地址)

    欢迎访问博主个人网站,记得收藏哦,点击查看 - - - >>>> 公众号推荐:计算机类毕业设计系统源码,IT技术文章分享,游戏源码,网页模板 小程序推荐:网站资源快速收录--百 ...

  6. 【形式化方法:VDM++系列】3.基于VDM++的图书管理系统需求定义

    接前文:http://www.cnblogs.com/Kassadin/p/4091040.html 1.Before We Start: 在开始图书管理系统需求定义之前,需要先进行一些说明. 1.1 ...

  7. vue.js快速搭建图书管理平台

      前  言 上一期简单讲解了vue的基本语法,这一次我们做一个小项目,搭建一个简单的图书管理平台,能够让我们更深刻的理解这门语言的妙用.   1.DEMO样式 首先我们需要搭建一个简单的demo样式 ...

  8. xml和集合混合使用-图书管理器

    package com.book; public class Book { private int id; //图书编号 private String name; //图书名称 private Str ...

  9. ML基础——搜索引擎与图书管理,百度与李彦宏

    本文始发于个人公众号:TechFlow 谈及机器学习,大家想必会有许多联想,比如最近火热的人工智能,再比如战胜李世石的AlphaGo,甚至还会有人联想起骇客帝国或者是机械公敌等经典机器人电影. 但实际 ...

随机推荐

  1. 关于连接服务器redis的教程

    第一步:下载RedisDesktopManager 这个百度一搜就有了,但是现在的版本ssh用不了 建议找可以用的版本,这个百度,懂得都懂. 第二步:服务器宝塔redis设置 在配置文件将bind 1 ...

  2. Golang使用swaggo自动生成Restful API文档

    #关于Swaggo 相信很多程序猿和我一样不喜欢写API文档.写代码多舒服,写文档不仅要花费大量的时间,有时候还不能做到面面具全.但API文档是必不可少的,相信其重要性就不用我说了,一份含糊的文档甚至 ...

  3. virtualbox nat 模式下连接虚拟机redis

    主要是使用端口转发的方法 如果你能使用xshell等工具连接这个方法基本一样  接着修改redis.conf文件的69 行(我使用的是5.0)将这里的地址修改为虚拟机的 ip 地址,这里我使用的是备份 ...

  4. NOIP 模拟七 考试总结

    T1匹配 签到大水题,这里有hash,kmp,ac自动机,还有后缀数组,后缀自动机任您挑选. 不过这个数据范围有些坑啊,re就很不爽.做法我还是比较倾向hash的,毕竟不论神魔字符算法,hash大都能 ...

  5. == 和 equals区别

    == equals是两种字符串的方式 区别 == 是比较两个对象的引用地址值 equals是比较两个对象的具体内容 示例 package com.oop.demo06; public class De ...

  6. Java多线程--实现同步的9种方法

    我们通常说的保持同步,其实就是对共享资源的保护.在单线程模型中, 我们永远不用担心"多个线程试图同时使用同一个资源的问题", 但是有了并发, 就有可能发生多个线程竞争同一个共享资源 ...

  7. 学习使用Wpf开源的文本编辑器—smithhtmleditor

    前言 本文主要介绍使用Wpf文本编辑器--smithhtmleditor. 编辑器使用 首先新建一个项目WpfEditor. 然后到Codeplex下载smithhtmleditor. 下载地址:ht ...

  8. 配置Internal Load balancer中VM的外网访问

    当在Azure中部署SQL VM时,处于安全考虑,不会配置VM的Public IP,会禁止外网的进出站访问,只允许从内部VNET,或者特定的内部IP访问.特别是当使用Azure Internal Lo ...

  9. javascript-jquery-更改jquery对象

    在许多情况下,jquery代码所做的事情变成了:生成jquery对象A,操作对jquery象A:更改为jquery对象B,操作jquery对象B:更改为jqueryC,操作jquery对象C..... ...

  10. Java只有值传递

    二哥,好久没更新面试官系列的文章了啊,真的是把我等着急了,所以特意过来催催.我最近一段时间在找工作,能从二哥的文章中学到一点就多一点信心啊! 说句实在话,离读者 trust you 发给我这段信息已经 ...