书店管理系统(C++初学者友好版)

写在前面

正如标题所示,本文是写给C++的初学者的一个书店管理系统实例。为了贴近目标读者的需求,本篇博客中的代码仅采用了初级的内容。比如:对于书的名字,采取了最简单粗暴的多维字符串存储而不是Trie树甚或AC自动机;对于所有排序操作,均采用简单粗暴的冒泡排序,而不是重载运算符后调用STL中的sort函数。力求简单易懂,为C++初学者提供良好的阅读体验。

此外,与我惯于对付的传统竞赛题不同的是,此类题目需要更多考虑到使用者的层次和需求。需要提供一定的使用指导和人机交互界面,还需要考虑到一些不合法的输入。因为本篇博客中的代码仅作抛砖引玉之用(以及本人的懒惰),代码中仅加入了有限的不合法输入判定,仍然存在非常多的不合法输入可以导致程序不能正常运行。同时,如果仅仅满足下方图片所示的要求,作为一个书店管理系统还是有所欠缺,可以加入诸如图形界面、撤销操作、修改操作等更多功能。

学海无涯,本人也不过先行几步。以上所述,就权作对后续学习的一点浅见。

题目要求

题目要求
(鉴于图片足够清晰,就不手打了)

主体思路

对于图书信息的储存,显然单独开若干个数组将书名、作者、出版社等信息分别储存并不是一种优美的做法(当然,从这句话可以看出这种解决方案依旧可行)。所以,我们应该将这些信息“绑定”在一起,相当于一种新的数据类型 b o o k book book。这种方法叫做定义结构体,将在下一个章节讲到。

当我们想要查找某一本书的时候,直接用for循环遍历已有的所有书并进行for循环暴力字符串匹配即可。

对于一本书的销售额、销售数量,同样可以定义两个变量一起“绑定”在 b o o k book book里,在排序时直接按照给定的关键字来排序。

至于进出货记录,可以直接开多维数组记录 进/出货、数量、交易额等信息,也可以定义结构体。

代码实现

定义结构体book

这一步很简单,掌握了相应的语法之后,只需要把你想要“绑定”在一起的各个变量、数组加进去即可。语法如下:

struct 定义的新数据类型名字
{
    
    
	变量1、变量2、……
	数组1、数组2、……
};

在本题中,按照题目要求,我们定义如下结构体:

struct book
{
    
    
    int number,publishDate[5],salesVolume;
    char name[50],publisher[50],author[50];
    double price,sales;
    bool discount;
}books[M];

注:右花括号与分号之间的books[M]是声明数组一个 b o o k book book类型的数组book books[M];的简化写法。

读入带空格字符串的函数

显然,书名、作者、出版社等信息都是带空格的字符串,不能简单地用scanf("%s",str)来代替。因此,我们需要单独编写对应函数。

在下方所示的函数中,传入参数时,传入了字符串数组的地址,这样就可以直接修改对应字符串数组。

第一个for循环筛除了字符串开头所有非数字且非字母的字符,其中isdigit()判断一个字符是否为数字,isalpha()判断一个字符是否为字母。

第二个for循环开始读入,直到读到换行符时停止读入。其中,(为了省事)第0个位置str[0]被我用来记录字符串的长度,str[1]~str[str[0]]为字符串内容。

void readString(char *str)
{
    
    
    char c=getchar();
    for(;!isdigit(c)&&!isalpha(c);c=getchar());
    for(str[0]=0;c!='\n';c=getchar())str[++str[0]]=c;
}

通过书名查找书的编号

这个就很暴力了,for循环即可。

值得注意的是,我们这里传入的数组是形参形式,函数内对数组的更改并不会影响原本的数组。

int enquireByName(char goal[])
{
    
    
    for(int i=1;i<=totalNumber;++i)
    {
    
    
        int flag=1;
        for(int j=1;j<=books[i].name[0];++j)
        if(books[i].name[j]!=goal[j]){
    
    flag=0;break;}
        if(flag)return i;
    }
    return 0;
}

进货函数

有了之前写的若干函数,就很简单了,先输入要进货的书的书名,在已有书中检索,如果书已存在,直接对数目进行修改;如果之前没有过这本书,就用读入带空格字符串函数读入各种信息储存到books数组里,并为新入库的书编号。在函数结尾,将进货记录保存到records数组里。

void purchase()
{
    
    
    int id=0,tmp=0;
    puts("-Please input the title of the book you want to stock (Press \"Enter\" to complete your input)");
    readString(tmpBook);
    if(id=enquireByName(tmpBook))
    {
    
    
        puts("-The information of this book has already been recorded in system");
        puts("-So you only need to input the number of newly purchased books");
        scanf("%d",&tmp),books[id].number+=tmp;
    }
    else
    {
    
    
        id=++totalNumber;
        for(int i=0;i<=tmpBook[0];++i)books[id].name[i]=tmpBook[i];
        puts("-This is a book that we never purchased before");
        puts("-Please input its detailed information");
        puts("-Please input the author of the book you want to stock (Press \"Enter\" to complete your input)");
        readString(books[totalNumber].author);
        puts("-Please input the publishing house of the book you want to stock (Press \"Enter\" to complete your input)");
        readString(books[totalNumber].publisher);
        puts("-Please input the price of the book you want to stock (Press \"Enter\" to complete your input)");
        scanf("%lf",&books[totalNumber].price);
        puts("-Is there a discount for this book (Input 0 for No or 1 for Yes)");
        scanf("%d",&books[totalNumber].discount);
        puts("-Please input the number of newly purchased books (Press \"Enter\" to complete your input)");
        scanf("%d",&books[totalNumber].number),tmp=books[totalNumber].number;
        puts("-Please input the publishing date of the book you want to stock (Press \"Enter\" to complete your input)");
        puts("-Please input as follow format: Year Month Day (3 numbers divided by space)");
        for(int i=1;i<=3;++i)
        scanf("%d",&books[totalNumber].publishDate[i]);
    }
    records[++cot][0]=0,records[cot][1]=id,records[cot][2]=tmp;
    puts("-Opration ends");
}

出货函数

出货函数和进货函数逻辑类似,都是在库存中查找,检查是否有足够的库存,再检查有无折扣,最后生成一条新的出货记录。

void sell()
{
    
    
    puts("-Please input the title of the book you want to sell (Press \"Enter\" to complete your input)");
    readString(tmpBook);
    int id=enquireByName(tmpBook),tmp,lev=0;
    if(!id){
    
    puts("-There is no book called this!");return;}
    if(books[id].discount)
    {
    
    
        puts("-This book can be discounted.");
        puts("-Please input the buyer's VIP level (Press \"Enter\" to complete your input)");
        scanf("%d",&lev);
        if(lev<0||lev>3){
    
    puts("-Invalid level!");return;}
    }
    else puts("-This book can't be discounted.");
    puts("-Please input the number of the book you want to sell (Press \"Enter\" to complete your input)");
    scanf("%d",&tmp);
    if(tmp>books[id].number){
    
    puts("-No enough books here");return;}
    books[id].number-=tmp;
    books[id].salesVolume+=tmp;
    books[id].sales+=tmp*VIP[lev]*books[id].price;
    records[++cot][0]=1,records[cot][1]=id,records[cot][2]=tmp,salesRecords[cot]=tmp*VIP[lev]*books[id].price;
    puts("-Opration ends");
}

查询某书的详细信息、销售情况、库存函数

这三个函数比较雷同,都是先通过书名查找,然后直接将结构体内记录的内容输出即可。如果是通过编号查找,则直接输出即可。

查询详细信息函数

void enquireBy(int p)//0-name 1-id
{
    
    
    int id;
    if(p)
    {
    
    
        puts("-Please input the title of the book you want to enquire (Press \"Enter\" to complete your input)");
        scanf("%d",&id);
        if(id<=0||id>totalNumber){
    
    puts("-There is no book numbered this!");return;}
        printBook(id);
        return;
    }
    puts("-Please input the title of the book you want to enquire (Press \"Enter\" to complete your input)");
    readString(tmpBook);
    id=enquireByName(tmpBook);
    if(!id){
    
    puts("-There is no book called this!");return;}
    printBook(id);
    puts("-Opration ends");
}

查询销售情况函数

void enquireSalesBy(int p)//0-name 1-id
{
    
    
    int id;
    if(p)
    {
    
    
        puts("-Please input the title of the book you want to enquire (Press \"Enter\" to complete your input)");
        scanf("%d",&id);
        if(id<=0||id>totalNumber){
    
    puts("-There is no book numbered this!");return;}
        printf("-Sell %d books for $%.2lf.\n",books[id].salesVolume,books[id].sales);
        return;
    }
    puts("-Please input the title of the book you want to enquire (Press \"Enter\" to complete your input)");
    readString(tmpBook);
    id=enquireByName(tmpBook);
    if(!id){
    
    puts("-There is no book called this!");return;}
    printf("-Sell %d books for $%.2lf.\n",books[id].salesVolume,books[id].sales);
    puts("-Opration ends");
}

查询库存函数

void enquireInventoryBy(int p)//0-name 1-id
{
    
    
    int id;
    if(p)
    {
    
    
        puts("-Please input the title of the book you want to enquire (Press \"Enter\" to complete your input)");
        scanf("%d",&id);
        if(id<=0||id>totalNumber){
    
    puts("-There is no book numbered this!");return;}
        printf("-The number of this book in inventory is %d.\n",books[id].number);
        return;
    }
    puts("-Please input the title of the book you want to enquire (Press \"Enter\" to complete your input)");
    readString(tmpBook);
    id=enquireByName(tmpBook);
    if(!id){
    
    puts("-There is no book called this!");return;}
    printf("-The number of this book in inventory is %d.\n",books[id].number);
    puts("-Opration ends");
}

查询进/出货记录函数

根据查找的类型,把records数组里的信息输出即可,这里出货额度我单独开了一个double类型的salesRecords数组。

void enquireRecords(int p)//0-purchase 1-sell
{
    
    
    if(p)puts("-Sell records:");
    else puts("-Purchase records:");
    for(int i=1;i<=cot;++i)
    if(records[i][0]==p)
    {
    
    
        if(p)printf("-Sell");
        else printf("-Purchase");
        printf(" %d ",records[i][2]);
        printString(books[records[i][1]].name);
        if(p)printf("for $%.2lf\n\n",salesRecords[i]);
    }
    puts("-Opration ends");
}

排序函数

因为这里有两种关键字排序方式,且book类型中有很多与排序操作无关的其他信息,最重要的是我直接把数组下标作为书的编号,所以books数组里的数据位置不能随意变动。

分析一下,实际上与排序有关的变量只有两个:排序所用关键字以及关键字对应的书的编号。只要有这两个信息,我们就可以实现排序功能。

因此,我们开一个临时二位数组tool来储存这两个信息,再使用冒泡排序以给定的关键字排序。

具体实现是,先把编号和所需关键字复制到tool数组中:

for(int i=1;i<=totalNumber;++i)
{
    
    
    tool[i][0]=i;
    tool[i][1]=p?books[i].sales:books[i].salesVolume;
}

这里第二行使用了三目运算,相当于一个if+else:

if(p)tool[i][1]=books[i].sales;
else tool[i][1]=books[i].salesVolume;

之后就是双重for循环实现的冒泡排序,注意在交换元素的时候,要将tool[i][0]tool[i][1]中的数据一起交换。

for(int i=1;i<=totalNumber;++i)for(int j=i+1;j<=totalNumber;++j)
    if(tool[i][1]<tool[j][1])swap(tool[i][1],tool[j][1]),swap(tool[i][0],tool[j][0]);

这样我们把tool数组按照tool[i][1]排好了序,tool[i][0]里就是对应的书的编号,按照该顺序输出编号对应书名即可。

for(int i=1;i<=totalNumber;++i)
    printString(books[(int)tool[i][0]].name);

注:因为销售额为小数类型,所以二位数组不能开int类型,但是double类型又不能作为数组下标,所以在代入books数组是要用(int)tool[i][0]转换数据类型(不偷懒的做法是定义一个结构体)。

完整代码:

double tool[M][2];
void sortAs(int p)//0-salesVolume 1-sales
{
    
    
    for(int i=1;i<=totalNumber;++i)
    {
    
    
        tool[i][0]=i;
        tool[i][1]=p?books[i].sales:books[i].salesVolume;
    }
    for(int i=1;i<=totalNumber;++i)for(int j=i+1;j<=totalNumber;++j)
    if(tool[i][1]<tool[j][1])swap(tool[i][1],tool[j][1]),swap(tool[i][0],tool[j][0]);
    for(int i=1;i<=totalNumber;++i)
    printString(books[(int)tool[i][0]].name);
    puts("-Opration ends");
}

其他函数

这些函数都比较浅显,不多做说明。

输出带空格字符串函数

这个函数是建立在读入函数的规则之上的。

void printString(char str[])
{
    
    
    for(int i=1;i<=str[0];++i)printf("%c",str[i]);
    putchar(10);
}

输出一本书的详细信息函数

void printBook(int id)
{
    
    
    puts("-The title of the book is:");
    printString(books[id].name);
    puts("-The author of the book is:");
    printString(books[id].author);
    puts("-The publishing house of the book is");
    printString(books[id].publisher);
    puts("-The price of the book is:");
    printf("$%.2lf\n",books[id].price);
    if(books[id].discount)puts("-There is a discount for this book");
    else puts("-There isn't a discount for this book");
    puts("-The publishing date of the book is:");
    for(int i=1;i<=3;++i)printf("%d ",books[id].publishDate[i]);putchar(10);
}

输出操作列表函数

void operationList()
{
    
    
    puts("-Operate by inputting the corresponding number as follow:");
    puts(" 1 Enquire a book's detailed information through its name.");
    puts(" 2 Enquire a book's sales.");
    puts(" 3 Enquire a book's inventory.");
    puts(" 4 Sort by sales.");
    puts(" 5 Sort by sales Volume.");
    puts(" 6 Enquire the records of stocking.");
    puts(" 7 Enquire the records of exporting.");
    puts(" 8 Purchase one kind of book.");
    puts(" 9 Sell one kind of book.");
    puts(" 0 Operation list.");
    puts("-Input a digit less than 0 to exit the system." );
}

完整代码

W e l c o m e   t o   u s e   P i B o o k   b o o k   m a n a g e m e n t   s y s t e m ! Welcome\ to\ use\ PiBook\ book\ management\ system! Welcome to use PiBook book management system!

#include<bits/stdc++.h>
using namespace std;
const int M=1005;
int totalNumber;
char tmpBook[50];
struct book
{
    
    
    int number,publishDate[5],salesVolume;
    char name[50],publisher[50],author[50];
    double price,sales;
    bool discount;
}books[M];
int records[M<<3][5],cot;//[0]:0-purchase 1-sell [1]:id [2]:number
double salesRecords[M<<3];
double VIP[]={
    
    1,0.9,0.8,0.6};
void readString(char *str)
{
    
    
    char c=getchar();
    for(;!isdigit(c)&&!isalpha(c);c=getchar());
    for(str[0]=0;c!='\n';c=getchar())str[++str[0]]=c;
}
void printString(char str[])
{
    
    
    for(int i=1;i<=str[0];++i)printf("%c",str[i]);
    putchar(10);
}
void printBook(int id)
{
    
    
    puts("-The title of the book is:");
    printString(books[id].name);
    puts("-The author of the book is:");
    printString(books[id].author);
    puts("-The publishing house of the book is");
    printString(books[id].publisher);
    puts("-The price of the book is:");
    printf("$%.2lf\n",books[id].price);
    if(books[id].discount)puts("-There is a discount for this book");
    else puts("-There isn't a discount for this book");
    puts("-The publishing date of the book is:");
    for(int i=1;i<=3;++i)printf("%d ",books[id].publishDate[i]);putchar(10);
}
int enquireByName(char goal[])
{
    
    
    for(int i=1;i<=totalNumber;++i)
    {
    
    
        int flag=1;
        for(int j=1;j<=books[i].name[0];++j)
        if(books[i].name[j]!=goal[j]){
    
    flag=0;break;}
        if(flag)return i;
    }
    return 0;
}
void purchase()
{
    
    
    int id=0,tmp=0;
    puts("-Please input the title of the book you want to stock (Press \"Enter\" to complete your input)");
    readString(tmpBook);
    if(id=enquireByName(tmpBook))
    {
    
    
        puts("-The information of this book has already been recorded in system");
        puts("-So you only need to input the number of newly purchased books");
        scanf("%d",&tmp),books[id].number+=tmp;
    }
    else
    {
    
    
        id=++totalNumber;
        for(int i=0;i<=tmpBook[0];++i)books[id].name[i]=tmpBook[i];
        puts("-This is a book that we never purchased before");
        puts("-Please input its detailed information");
        puts("-Please input the author of the book you want to stock (Press \"Enter\" to complete your input)");
        readString(books[totalNumber].author);
        puts("-Please input the publishing house of the book you want to stock (Press \"Enter\" to complete your input)");
        readString(books[totalNumber].publisher);
        puts("-Please input the price of the book you want to stock (Press \"Enter\" to complete your input)");
        scanf("%lf",&books[totalNumber].price);
        puts("-Is there a discount for this book (Input 0 for No or 1 for Yes)");
        scanf("%d",&books[totalNumber].discount);
        puts("-Please input the number of newly purchased books (Press \"Enter\" to complete your input)");
        scanf("%d",&books[totalNumber].number),tmp=books[totalNumber].number;
        puts("-Please input the publishing date of the book you want to stock (Press \"Enter\" to complete your input)");
        puts("-Please input as follow format: Year Month Day (3 numbers divided by space)");
        for(int i=1;i<=3;++i)
        scanf("%d",&books[totalNumber].publishDate[i]);
    }
    records[++cot][0]=0,records[cot][1]=id,records[cot][2]=tmp;
    puts("-Opration ends");
}
void enquireBy(int p)//0-name 1-id
{
    
    
    int id;
    if(p)
    {
    
    
        puts("-Please input the title of the book you want to enquire (Press \"Enter\" to complete your input)");
        scanf("%d",&id);
        if(id<=0||id>totalNumber){
    
    puts("-There is no book numbered this!");return;}
        printBook(id);
        return;
    }
    puts("-Please input the title of the book you want to enquire (Press \"Enter\" to complete your input)");
    readString(tmpBook);
    id=enquireByName(tmpBook);
    if(!id){
    
    puts("-There is no book called this!");return;}
    printBook(id);
    puts("-Opration ends");
}
void enquireSalesBy(int p)//0-name 1-id
{
    
    
    int id;
    if(p)
    {
    
    
        puts("-Please input the title of the book you want to enquire (Press \"Enter\" to complete your input)");
        scanf("%d",&id);
        if(id<=0||id>totalNumber){
    
    puts("-There is no book numbered this!");return;}
        printf("-Sell %d books for $%.2lf.\n",books[id].salesVolume,books[id].sales);
        return;
    }
    puts("-Please input the title of the book you want to enquire (Press \"Enter\" to complete your input)");
    readString(tmpBook);
    id=enquireByName(tmpBook);
    if(!id){
    
    puts("-There is no book called this!");return;}
    printf("-Sell %d books for $%.2lf.\n",books[id].salesVolume,books[id].sales);
    puts("-Opration ends");
}
void enquireInventoryBy(int p)//0-name 1-id
{
    
    
    int id;
    if(p)
    {
    
    
        puts("-Please input the title of the book you want to enquire (Press \"Enter\" to complete your input)");
        scanf("%d",&id);
        if(id<=0||id>totalNumber){
    
    puts("-There is no book numbered this!");return;}
        printf("-The number of this book in inventory is %d.\n",books[id].number);
        return;
    }
    puts("-Please input the title of the book you want to enquire (Press \"Enter\" to complete your input)");
    readString(tmpBook);
    id=enquireByName(tmpBook);
    if(!id){
    
    puts("-There is no book called this!");return;}
    printf("-The number of this book in inventory is %d.\n",books[id].number);
    puts("-Opration ends");
}
void sell()
{
    
    
    puts("-Please input the title of the book you want to sell (Press \"Enter\" to complete your input)");
    readString(tmpBook);
    int id=enquireByName(tmpBook),tmp,lev=0;
    if(!id){
    
    puts("-There is no book called this!");return;}
    if(books[id].discount)
    {
    
    
        puts("-This book can be discounted.");
        puts("-Please input the buyer's VIP level (Press \"Enter\" to complete your input)");
        scanf("%d",&lev);
        if(lev<0||lev>3){
    
    puts("-Invalid level!");return;}
    }
    else puts("-This book can't be discounted.");
    puts("-Please input the number of the book you want to sell (Press \"Enter\" to complete your input)");
    scanf("%d",&tmp);
    if(tmp>books[id].number){
    
    puts("-No enough books here");return;}
    books[id].number-=tmp;
    books[id].salesVolume+=tmp;
    books[id].sales+=tmp*VIP[lev]*books[id].price;
    records[++cot][0]=1,records[cot][1]=id,records[cot][2]=tmp,salesRecords[cot]=tmp*VIP[lev]*books[id].price;
    puts("-Opration ends");
}
void enquireRecords(int p)//0-purchase 1-sell
{
    
    
    if(p)puts("-Sell records:");
    else puts("-Purchase records:");
    for(int i=1;i<=cot;++i)
    if(records[i][0]==p)
    {
    
    
        if(p)printf("-Sell");
        else printf("-Purchase");
        printf(" %d ",records[i][2]);
        printString(books[records[i][1]].name);
        if(p)printf("for $%.2lf\n\n",salesRecords[i]);
    }
    puts("-Opration ends");
}
double tool[M][2];
void sortAs(int p)//0-salesVolume 1-sales
{
    
    
    for(int i=1;i<=totalNumber;++i)
    {
    
    
        tool[i][0]=i;
        tool[i][1]=p?books[i].sales:books[i].salesVolume;
    }
    for(int i=1;i<=totalNumber;++i)for(int j=i+1;j<=totalNumber;++j)
    if(tool[i][1]<tool[j][1])swap(tool[i][1],tool[j][1]),swap(tool[i][0],tool[j][0]);
    for(int i=1;i<=totalNumber;++i)
    printString(books[(int)tool[i][0]].name);
    puts("-Opration ends");
}
void operationList()
{
    
    
    puts("-Operate by inputting the corresponding number as follow:");
    puts(" 1 Enquire a book's detailed information through its name.");
    puts(" 2 Enquire a book's sales.");
    puts(" 3 Enquire a book's inventory.");
    puts(" 4 Sort by sales.");
    puts(" 5 Sort by sales Volume.");
    puts(" 6 Enquire the records of stocking.");
    puts(" 7 Enquire the records of exporting.");
    puts(" 8 Purchase one kind of book.");
    puts(" 9 Sell one kind of book.");
    puts(" 0 Operation list.");
    puts("-Input a digit less than 0 to exit the system." );
}
int main()
{
    
    
    puts("-Welcome to use PiBook book management system!");
    operationList();
    for(int op,tmp;;)
    {
    
    
        scanf("%d",&op);
        if(op<0)break;
        if(op>9){
    
    puts("-Invalid input!");continue;}
        if(op&&op<=3)
        {
    
    
            puts("-Input the way you want to enquire the book by (0-name 1-ID)");
            scanf("%d",&tmp);
        }
        if(!op)operationList();
        else if(op==1)enquireBy(tmp);
        else if(op==2)enquireSalesBy(tmp);
        else if(op==3)enquireInventoryBy(tmp);
        else if(op==4)sortAs(1);
        else if(op==5)sortAs(0);
        else if(op==6)enquireRecords(0);
        else if(op==7)enquireRecords(1);
        else if(op==8)purchase();
        else if(op==9)sell();
        puts("================================");
    }
    puts("-Thanks for using PiBook book managemnet system!");
    system("pause");
}

结语

正如前面所说,人机交互系统的难点就落在人机交互上,此次编程我也算深有感触。如果删去各种puts()出来的提示信息和非法输入判断,我可以很快结束代码的编写,但除我以外也就没人会使用这个系统了。即便我花费了大量的时间在交互上,这份代码依然存在我前面所说的种种不足,种种bug。当用户完全按照给定指令操作时或许无伤大雅,但是面对刁钻的甲方,以及可能存在的恶意攻击,这份代码很容易就会陷入故障。

管中窥豹,我不得不感慨想要做出一个成熟、完备而安全,能够制止各种非法操作且具有高人性化交互的系统是多么的困难。在IT学习上,我还有很长的路要走……

猜你喜欢

转载自blog.csdn.net/ShadyPi/article/details/111505258