首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >教育“图书馆”工程

教育“图书馆”工程
EN

Code Review用户
提问于 2014-01-22 10:46:01
回答 2查看 187关注 0票数 6

这项任务是非常开放的,我们只需要涵盖4件事情:

  1. 文件读写
  2. 使用结构
  3. 导出为HTML格式
  4. 使用常见的排序算法

因此,我决定创建一个具有以下功能的“库”:

  • 读取输入文件"Library.dat“
  • 列出控制台上的书籍
  • 把书按名字分类
  • 增加新书
  • 按ID删除一本书
  • 借阅一本书

所以我是C的初学者,我很乐意回顾一下如何改进我的项目。

输入文件:

代码语言:javascript
复制
1;v;121212;121212;1
2;a;121212;121212;0
4;e;121212;121212;1
6;d;121212;121212;1
7;w;121212;121212;0
8;x;121212;121212;1
9;c;121212;121212;1

我的配置文件:

代码语言:javascript
复制
//
//  Library.config
//
//  Created by Me on 03.12.13.
//  Copyright (c) 2013 Me. All rights reserved.
//

// DEFINE

#define MAX_STR_LEN 256
#define MAX_BOOKS 10
#define DATE_LEN 8
#define PATH "/users/xxxxxxx/desktop/Library/Library.dat"

我的守则:

代码语言:javascript
复制
/*************************************************
 * Library Application
 *
 * Author:  Me
 * Date:    26.11.2013
 * Version: BETA
 *
 *
 *************************************************/

/****************************
 *  OSX: fpurge(stdin) insted of fflush(stdin)
 *
 *
 *
 *****************************/



/* TODOs:
 *
 * Search Function
 * Colors (conio.h)?
 *
 *
 */

/* header files for access to specialized functions*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "config.h"



typedef struct {
    int ID;
    char name[MAX_STR_LEN];
    char dateIn[DATE_LEN];
    char dateOut[DATE_LEN];
    int isIn;
}book;

struct book{
    int ID;
    char name[MAX_STR_LEN];
    char dateIn[DATE_LEN];
    char dateOut[DATE_LEN];
    int isIn;
};


/* array of my books */
struct book books[MAX_BOOKS];

/* PROTOTYPE OF FUNCTIONS */
int readBookFile();
void printBookList();
void addBook();
void mutateBook(book b);    //TODO
int getNextID();
book enterBookData();
void delBook();
int writeBookFile();
void menu();
void checkOut();
void checkIn();
void sort();

/****************************************************
 * Function: main()
 *
 * Description: What is there to explain..
 *
 * Parameters:  argv (the string of all parameters),
 *              argc (the count of all input aruments)
 *
 * return parameters: int, 0 = OK, 1 = Error
 ****************************************************/
int main(int argc, char **argv)
{
    /* declaration of the return parameter */
    int isOK = 0;

    /* first read the input file for the Library */
    isOK = readBookFile();

    /* the menu is the main controll structure */
    menu();

    /* last write the changes made to the Library file */
    isOK = writeBookFile();

    /* pause the system, so we see its over */
    //system("pause");
    system( "read -n 1 -s -p \"Press any key to continue...\"" );


    /* return 1 = Error, 0 = ok */
    return isOK;
}


/*********************************************
 * Function: readBookFile()
 *
 * Description: Read the csv file into a books arrray
 *
 * Parameters: none
 *
 * Return parameters: none
 * ********************************************/
int readBookFile()
{
    /* FileStream for the Library File */
    FILE *bookFile;

    /* allocation of the buffer for every line in the File */
    char *buf = malloc(MAX_STR_LEN);
    char *tmp;

    /* if the space could not be allocaed, return an error */
    if (buf == NULL) {
        printf ("No memory\n");
        return 1;
    }

    /* chack if the file can really be loaded into the stream */
    if ( ( bookFile = fopen(  PATH , "r" ) ) == NULL ) //Reading a file
    {
        printf( "File could not be opened.\n" );
        return 1;
    }

    int i = 0;
    /* read every line of the inputfile to an element of the books array */
    while (fgets(buf, 255, bookFile) != NULL)
    {
        /* this is to check if the files last line was reached last turn */
        if(ferror(bookFile))
        {
            break;
        }

        /* if we don't chech for newline we skip every second line */
        if ((strlen(buf)>0) && (buf[strlen (buf) - 1] == '\n'))
            buf[strlen (buf) - 1] = '\0';

        /* read the ID and cast it to an Integer  */
        tmp = strtok(buf, ";");
        books[i].ID = atoi(tmp);

        /*  to give the value of the tmp part of the csv string
            to books[i] we need to copy the string! */
        tmp = strtok(NULL, ";");
        strcpy( books[i].name, tmp);

        tmp = strtok(NULL, ";");
        strcpy(books[i].dateIn, tmp);

        tmp = strtok(NULL, ";");
        strcpy(books[i].dateOut, tmp);

        tmp = strtok(NULL, ";");
        books[i].isIn = atoi(tmp);

        //      printf("index i= %i  ID: %i, %s, %s, %s \n",i, books[i].ID , books[i].name, books[i].dateIn , books[i].dateOut);
        /* increment for next book in books[] */
        i++;

    }

    /* free the buffer */
    free(buf);

    /* close the filestream */
    fclose(bookFile);
    return 0;
}

/*********************************************
 * Function: printBookList()
 *
 * Description: Print the Array of books to the console
 *
 * Parameters: none
 *
 * Return parameters: none
 * ********************************************/
void printBookList()
{

    int i;
    sort();
    printf("ID: \t  Title:  \t\t  Dateout:  \tDate in:\tOut? \n");
    printf("------------------------------------------------------------------------\n");
    for (i = 0; i <= MAX_BOOKS; i++)
    {
        /* only do this until the last is printed, as 256 are allocated by default */
        if (books[i].ID != 0)
        /* the %.15s means only the first 15 characters of the string will be printed  */
            printf("ID: %i\t  %.15s  \t  %s  \t%s\t\t%i \n", books[i].ID , books[i].name, books[i].dateIn , books[i].dateOut, books[i].isIn);
        else
            break;
    }
    printf("------------------------------------------------------------------------\n");

}

/*********************************************
 * Function: addBook()
 *
 * Description: add a new book to the books[] array
 *
 * Parameters: none
 *
 * Return parameters: none
 * ********************************************/
void addBook()
{
    /* the function enterBookData return the new book */
    book bin = enterBookData();

    int newID = 0;
    /* the getNextID returns the next free ID number */
    newID = getNextID();

    books[newID].ID= newID + 1;
    strcpy(books[newID].name, bin.name);
    strcpy(books[newID].dateIn, bin.dateIn);
    strcpy(books[newID].dateOut, bin.dateOut);
    books[newID].isIn =  bin.isIn;

}


/*********************************************
 * Function: getNextID()
 *
 * Description: returns the next free index of the books[] array
 *
 * Parameters: none
 *
 * Return parameters: int, the next free index of books[]
 * ********************************************/
int getNextID()
{
    int i;

    for (i = 0; i <= MAX_BOOKS; i++)
    {
        if (books[i].ID == 0)
            break;
    }
    return i;
}


/*********************************************
 * Function: enterBookData()
 *
 * Description: get the user input of every Attribute
 *
 * Parameters: none
 *
 * Return parameters: the new book
 * ********************************************/
book enterBookData()
{
    book b1 = {0,"","",""};
    char tmp[MAX_STR_LEN];

    printf("Titel: ");
    fgets(tmp, MAX_STR_LEN, stdin);

    if ((strlen(tmp)>0) && (tmp[strlen (tmp) - 1] == '\n'))
        tmp[strlen (tmp) - 1] = '\0';

    strcpy(b1.name, tmp);

    /* free the standard in, ohterwise you will encounter problems if the enterd string is too long  */
    fpurge(stdin);

    printf("datein (DDMMYY):");
    fgets(tmp, DATE_LEN, stdin);

    if ((strlen(tmp)>0) && (tmp[strlen (tmp) - 1] == '\n'))
        tmp[strlen (tmp) - 1] = '\0';

    strcpy(b1.dateIn, tmp);

    fpurge(stdin);

    printf("dateout (DDMMYY): ");
    fgets(tmp, DATE_LEN, stdin);

    if ((strlen(tmp)>0) && (tmp[strlen (tmp) - 1] == '\n'))
        tmp[strlen (tmp) - 1] = '\0';

    strcpy(b1.dateOut, tmp);

    fpurge(stdin);

    printf("is it in? (1 = yes, 0 = no): ");
    fgets(tmp, 2, stdin);

    if ((strlen(tmp)>0) && (tmp[strlen (tmp) - 1] == '\n'))
        tmp[strlen (tmp) - 1] = '\0';

    b1.isIn=  atoi(tmp);

    fpurge(stdin);

    return b1;
}

/*********************************************
 * Function: delBook()
 *
 * Description: deletes the index of the books[] array the user choose
 *
 * Parameters: none
 *
 * Return parameters: none
 * ********************************************/
void delBook()
{
    char ids[3];
    int id = 0, c;
    printf("Enter linenumber of book to delete. \n");

    fgets(ids, MAX_STR_LEN, stdin);

    if ((strlen(ids)>0) && (ids[strlen (ids) - 1] == '\n'))
        ids[strlen (ids) - 1] = '\0';

    id= atoi(ids);
    if (id > MAX_BOOKS)
        id = MAX_BOOKS;

    for ( c = id - 1 ; c < MAX_BOOKS - 1 ; c++ )
        books[c] = books[c+1];

    fflush(stdin);
}

/*********************************************
 * Function: writeBookFile()
 *
 * Description: writes the array of books[] to the file (overwrites)
 *
 * Parameters: none
 *
 * Return parameters: int, could the file be written? 1 = no, 0 = yes
 * ********************************************/
int writeBookFile()
{
    /* FileStream for the Library File */
    FILE *bookFile;

    if ( ( bookFile = fopen( PATH, "w" ) ) == NULL ) //Reading a file
    {
        printf( "File could not be opened.\n" );
        return 1;
    }

    int i;
    for(i = 0; i < MAX_BOOKS;i++)
    {

        if(books[i].ID != 0)
        {
            fprintf(bookFile, "%i;%s;%s;%s;%i\n", books[i].ID, books[i].name, books[i].dateIn, books[i].dateOut, books[i].isIn);
        }
    }

    fclose(bookFile);
    return 0;
}

/*********************************************
 * Function: menu()
 *
 * Description: the main menu, logic and printfs
 *
 * Parameters: none
 *
 * Return parameters: none
 * ********************************************/
void menu()
{
    char choice[1];

    while(choice[0] != '5')
    {
        system("clear");
        //printf("\33[2J");
        printBookList();

        printf("1. Add book    \n");
        printf("2. Delete book \n");
        printf("3. Check out book \n");
        printf("4. Check in book \n");
        printf("5. Quit        \n");
        printf("Please enter the number of your choice: ");
        fgets(choice, 2, stdin);
        fpurge(stdin);

        switch(choice[0])
        {
            case '1':
            {
                addBook();
                break;
            }

            case '2':
            {
                delBook();
                break;
            }

            case '3':
            {
                checkOut();
                break;
            }

            case '4':
            {
                checkIn();
                break;
            }

        }

    }

}

/*********************************************
 * Function: checkOut()
 *
 * Description: To check out a book, the user chooses the ID and then sets the isIn attribute of the book to 0
 *
 * Parameters: none
 *
 * Return parameters: none
 * ********************************************/
void checkOut()
{
    char tmp[3];
    int i = 0;
    printf("Please enter the ID of the book beeing checked out: ");

    fgets(tmp, 4, stdin);

    if ((strlen(tmp)>0) && (tmp[strlen (tmp) - 1] == '\n'))
        tmp[strlen (tmp) - 1] = '\0';

    fflush(stdin);

    for (i = 0; i < MAX_BOOKS;i++)
    {
        if(books[i].ID == atoi(tmp))
        {
            if (books[i].isIn == 0)
            {
                printf("Book allready checked out!\n");
                system("pause");
            }
            else
            {
                books[i].isIn = 0;
            }
            break;
        }

    }


}

/*********************************************
 * Function: checkIn()
 *
 * Description: To check in a book, the user chooses the ID and then sets the isIn attribute of the book to 1
 *
 * Parameters: none
 *
 * Return parameters: none
 * ********************************************/
void checkIn()
{
    char tmp[3];
    int i = 0;
    printf("Enter ID of the book beeing returned: ");

    fgets(tmp, 4, stdin);

    if ((strlen(tmp)>0) && (tmp[strlen (tmp) - 1] == '\n'))
        tmp[strlen (tmp) - 1] = '\0';

    fflush(stdin);

    for (i = 0; i < MAX_BOOKS;i++)
    {
        if(books[i].ID == atoi(tmp))
        {
            if (books[i].isIn == 1)
            {
                printf("Book allready checked here!\n");
                system("pause");
            }
            else
            {
                books[i].isIn = 1;
            }
            break;
        }

    }
}


/*********************************************
 * Function: sort()
 *
 * Description: sorts the Array of books by ID
 * Algorythmus: Bubblsort
 * Parameters: none
 *
 * Return parameters: none
 * ********************************************/

    void sort()
    {
        /* first find out how many indexes there are */
        int h;
        for (h = 0; h< MAX_BOOKS; h++)
        {
            if (books[h].ID == 0)
            {
                break;
            }
        }

        int j =  0;

        int swaped = 1;
        struct book temp;
        while (swaped == 1)       //bubble sort on the book name
        {
            swaped = 0;
            for(j=0;j< h -1 ;j++)
            {

                if(books[j].ID > books[j + 1].ID)
                {

                    //copy to temp val
                    temp.ID = books[j].ID;
                    strcpy(temp.name,books[j].name);
                    strcpy(temp.dateIn,books[j].dateIn);
                    strcpy(temp.dateOut,books[j].dateOut);
                    temp.isIn = books[j].isIn;


                    //copy next val
                    books[j].ID = books[j + 1].ID;
                    strcpy(books[j].name,books[j + 1].name);
                    strcpy(books[j].dateIn,books[j + 1].dateIn);
                    strcpy(books[j].dateOut,books[j + 1].dateOut);
                    books[j].isIn = books[j + 1].isIn;


                    //copy back temp val
                    books[j + 1].ID = temp.ID;
                    strcpy(books[j+ 1].name,temp.name);
                    strcpy(books[j + 1].dateIn,temp.dateIn);
                    strcpy(books[j + 1].dateOut,temp.dateOut);
                    books[j + 1].isIn = temp.isIn;


                    swaped = 1;

                }
            }
        }

    }

void mutateBook(book b)
{
    printf("What do you want to change?\n");

}
EN

回答 2

Code Review用户

回答已采纳

发布于 2014-01-23 13:44:35

代码语言:javascript
复制
#define MAX_STR_LEN 256
#define MAX_BOOKS 10
#define DATE_LEN 8

你如何选择这些数字?您是否编码确保文件中的数据不超过这些数字?

实际上,您的readBookFile函数有一个错误:如果一本书的标题几乎是MAX_STR_LEN,那么整个文件行的长度要大于MAX_STR_LEN,并且不适合缓冲区。

代码语言:javascript
复制
/* array of my books */

见杰克逊的回答。

代码语言:javascript
复制
/* PROTOTYPE OF FUNCTIONS */

如果在文件末尾定义main,那么您可能不需要这些函数声明,因为相反,在从main中使用函数定义之前,已经定义了函数定义,而不是main。

代码语言:javascript
复制
 * Description: What is there to explain..
 *
 * Parameters:  argv (the string of all parameters),
 *              argc (the count of all input aruments)

太多无用的评论了吗?

代码语言:javascript
复制
isOK = readBookFile();

如果isOK失败,您不会测试readBookFile,也不会做任何恢复或退出的操作(例如,打印错误消息并在尝试编写之前尽早返回)。

通常,分配给一个变量,然后不读取/使用变量中的值是可疑的:如果您不读取/使用它,为什么一开始就分配/记住它?

代码语言:javascript
复制
system( "read -n 1 -s -p \"Press any key to continue...\"" );

我不知道system函数。为什么不选择printf呢?

代码语言:javascript
复制
FILE *bookFile;

我喜欢把我的变量定义推迟到后面的代码行(就在if ( ( bookFile语句之前),这时我实际上可以给它赋值。通常,任何未初始化的变量都是危险的。

代码语言:javascript
复制
printf ("No memory\n");

您使用的空格不一致:普通样式在函数名和它的开括号之间没有空格。

代码语言:javascript
复制
while (fgets(buf, 255, bookFile) != NULL)

我想你是说MAX_STR_LEN而不是255。如果您更改了MAX_STR_LEN的定义,该怎么办?您不希望在其他源代码中寻找要更改的其他255实例。

另外,为了修复我前面提到的错误,buf和fget应该使用MAX_BOOK_LEN而不是MAX_STR_LEN:

代码语言:javascript
复制
#define MAX_BOOK_LEN sizeof(Book) + 1

基于MAX_BOOK_LEN ( Book )的这个表达式假定Book.title是Book中的一个数组(目前是这样),而不是指向通过malloc分配的变量/无限长标题的指针(您可能会将其更改为将来的某个时候)。

代码语言:javascript
复制
    /* if we don't chech for newline we skip every second line */
    if ((strlen(buf)>0) && (buf[strlen (buf) - 1] == '\n'))
        buf[strlen (buf) - 1] = '\0';

你给strlen(buf)打了好几次电话。如果您希望它更快(非常快),那么只调用一次:

代码语言:javascript
复制
    size_t buflen = strlen(buf);
    if ((buflen > 0) && (buf[buflen - 1] == '\n'))
        buf[buflen - 1] = '\0';
代码语言:javascript
复制
if(ferror(bookFile))

根据我对一些文件的解读,这是不必要的:因为如果有任何错误,那么fgets将返回null。

您可能希望在返回之前调用它,这样您就可以使用它的返回代码来判断从输入文件读取时是否有错误:

代码语言:javascript
复制
/* check for error */
int returncode = ferror(bookFile);

/* free the buffer */
free(buf);

/* close the filestream */
fclose(bookFile);

return returncode; /* 0 if all reads were successful */
代码语言:javascript
复制
/* the getNextID returns the next free ID number */
newID = getNextID();

books[newID].ID= newID + 1;

这让人困惑:我想知道,为什么要在使用之前增加下一个ID?您不想增加当前ID,还是不增加到下一个ID?

为了避免这种混淆,也许可以将getNextID()函数重命名为getEmptyIndex(),并在newID = getEmptyIndex() + 1;语句中添加一个注释,说明索引是基于0的,而ID是基于1的。

代码语言:javascript
复制
printf("datein (DDMMYY):");

也许您应该验证输入内容的格式和长度,并要求用户重试或退出,如果语法错误。

有几个地方,即在addBook和排序中,您可以使用一系列语句,如:

代码语言:javascript
复制
books[newID].ID= newID + 1;
strcpy(books[newID].name, bin.name);
strcpy(books[newID].dateIn, bin.dateIn);
strcpy(books[newID].dateOut, bin.dateOut);
books[newID].isIn =  bin.isIn;

如果将来向Book (例如,作者)添加一个新字段会发生什么?您必须在几个地方更改这些语句。最好有一个新的普通子程序,

代码语言:javascript
复制
void memcpyBook(Book* toBook, const Book* fromBook) { ... }

我担心把ID链接到索引。如果您做了以下操作:

  1. 创作或阅读几本书
  2. 从数组中间删除一些书籍
  3. 把书分类

然后ID不再匹配数组索引。如果然后添加另一本书,您将找到空的索引,并可能创建一个重复的图书ID。

票数 3
EN

Code Review用户

发布于 2014-01-22 15:26:40

你不需要书的两个定义:

代码语言:javascript
复制
typedef struct {..} book ;
struct book {..} ;

我通常要做的是:

代码语言:javascript
复制
typedef struct Book {
...
} Book ;

这使您可以编写以下任意一种:

代码语言:javascript
复制
struct Book b1 ;
Book b1 ;

在这两种情况下都有一个图书结构。这也意味着我可以在一个图书结构中引用一个图书结构:

代码语言:javascript
复制
typedef struct Book {
   ...
   struct Book* prequel ;
} Book ;

由于大多数编码标准都要求structs和typedefs有一个初始大写字母,所以我会将结构和类型的名称更改为Book。

在很多地方,您都会看到ty对联和结构给出了不同的名称,

代码语言:javascript
复制
typedef struct Book_ {...} Book_T ;

这取决于您决定什么是最容易使用的;我说过我喜欢对两者使用相同的名称。

另一件要看的是readBookFile函数,通过阅读代码,我认为您需要查看以下内容:

您可以使用malloc()来分配文件被读取到的缓冲区;但是,您从未释放由malloc分配的内存。这是一个很好的实践,确保每个malloc最终都在通过函数的每条路径中被释放。

您不检查malloc的返回值;在像您这样的程序中,malloc调用不太可能失败;但是,您应该检查返回值。

你需要在这里使用malloc吗?如果在堆栈上分配缓冲区,则不需要担心释放它。

对fget()的调用中包含魔法号255,您可以而且应该只使用您在malloc中创建和使用的MAX_STR_LEN #define。阅读fget()的手册页,它会告诉您,函数已经知道最后一个字符被重新定义为终止NULL。

如果没有更多的令牌,strtok()函数调用返回NULL,这也是您应该检查的内容。

当对每个字段调用strtok()时,几乎相同的代码重复了几次,您应该考虑将其提取到它自己的函数中。

票数 4
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/39795

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档