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库的使用的更多相关文章
- 实例:建立图书借阅系统的UML模型
1.需求分析 图书借阅系统的组成 2.具体的功能详细描述: (1)管理员登录系统,进入借书工作状态,等待借书处理. (2)读者找到所需图书,在借书处上刷卡机上刷卡. (3)管理员对借阅证进行资格审查. ...
- Linux系统下安装ncurses库
ncurses库是一个Linux系统下的图形支持的函数库,字符终端处理库,包括面板和菜单. 今天在安装ncurses库的时候遇到了一些问题,现将遇到的问题所叙如下: 首先说明:本次安装采用的是源码包的 ...
- 个人项目开源之Django图书借阅系统源代码
Django写的模拟图书借阅系统源代码已开源到码云 源代码
- 基于web的图书管理系统设计与实现
原文链接:基于web的图书管理系统设计与实现 系统演示链接:点击这里查看演示 01 系统简述 图书管理系统就是利用计算机,结合互联网对图书进行结构化.自动化管理的一种软件,来提高对图书的管理效 ...
- 基于web的图书管理系统设计与实现(附演示地址)
欢迎访问博主个人网站,记得收藏哦,点击查看 - - - >>>> 公众号推荐:计算机类毕业设计系统源码,IT技术文章分享,游戏源码,网页模板 小程序推荐:网站资源快速收录--百 ...
- 【形式化方法:VDM++系列】3.基于VDM++的图书管理系统需求定义
接前文:http://www.cnblogs.com/Kassadin/p/4091040.html 1.Before We Start: 在开始图书管理系统需求定义之前,需要先进行一些说明. 1.1 ...
- vue.js快速搭建图书管理平台
前 言 上一期简单讲解了vue的基本语法,这一次我们做一个小项目,搭建一个简单的图书管理平台,能够让我们更深刻的理解这门语言的妙用. 1.DEMO样式 首先我们需要搭建一个简单的demo样式 ...
- xml和集合混合使用-图书管理器
package com.book; public class Book { private int id; //图书编号 private String name; //图书名称 private Str ...
- ML基础——搜索引擎与图书管理,百度与李彦宏
本文始发于个人公众号:TechFlow 谈及机器学习,大家想必会有许多联想,比如最近火热的人工智能,再比如战胜李世石的AlphaGo,甚至还会有人联想起骇客帝国或者是机械公敌等经典机器人电影. 但实际 ...
随机推荐
- 关于连接服务器redis的教程
第一步:下载RedisDesktopManager 这个百度一搜就有了,但是现在的版本ssh用不了 建议找可以用的版本,这个百度,懂得都懂. 第二步:服务器宝塔redis设置 在配置文件将bind 1 ...
- Golang使用swaggo自动生成Restful API文档
#关于Swaggo 相信很多程序猿和我一样不喜欢写API文档.写代码多舒服,写文档不仅要花费大量的时间,有时候还不能做到面面具全.但API文档是必不可少的,相信其重要性就不用我说了,一份含糊的文档甚至 ...
- virtualbox nat 模式下连接虚拟机redis
主要是使用端口转发的方法 如果你能使用xshell等工具连接这个方法基本一样 接着修改redis.conf文件的69 行(我使用的是5.0)将这里的地址修改为虚拟机的 ip 地址,这里我使用的是备份 ...
- NOIP 模拟七 考试总结
T1匹配 签到大水题,这里有hash,kmp,ac自动机,还有后缀数组,后缀自动机任您挑选. 不过这个数据范围有些坑啊,re就很不爽.做法我还是比较倾向hash的,毕竟不论神魔字符算法,hash大都能 ...
- == 和 equals区别
== equals是两种字符串的方式 区别 == 是比较两个对象的引用地址值 equals是比较两个对象的具体内容 示例 package com.oop.demo06; public class De ...
- Java多线程--实现同步的9种方法
我们通常说的保持同步,其实就是对共享资源的保护.在单线程模型中, 我们永远不用担心"多个线程试图同时使用同一个资源的问题", 但是有了并发, 就有可能发生多个线程竞争同一个共享资源 ...
- 学习使用Wpf开源的文本编辑器—smithhtmleditor
前言 本文主要介绍使用Wpf文本编辑器--smithhtmleditor. 编辑器使用 首先新建一个项目WpfEditor. 然后到Codeplex下载smithhtmleditor. 下载地址:ht ...
- 配置Internal Load balancer中VM的外网访问
当在Azure中部署SQL VM时,处于安全考虑,不会配置VM的Public IP,会禁止外网的进出站访问,只允许从内部VNET,或者特定的内部IP访问.特别是当使用Azure Internal Lo ...
- javascript-jquery-更改jquery对象
在许多情况下,jquery代码所做的事情变成了:生成jquery对象A,操作对jquery象A:更改为jquery对象B,操作jquery对象B:更改为jqueryC,操作jquery对象C..... ...
- Java只有值传递
二哥,好久没更新面试官系列的文章了啊,真的是把我等着急了,所以特意过来催催.我最近一段时间在找工作,能从二哥的文章中学到一点就多一点信心啊! 说句实在话,离读者 trust you 发给我这段信息已经 ...