Building a Simple Library Management System in C
Embarking on a Practical Project
Welcome to the 152nd installment of our C Language Series! In this post, we're diving into a fun and highly practical project: creating a mini-library management system. This hands-on exercise will consolidate your understanding of fundamental C concepts, including structures, arrays, functions, and basic user interaction. By the end, you'll have a console-based application capable of managing a collection of books.
This project is an excellent stepping stone for understanding how to organize data and build modular applications in C. We'll start by defining the core data structure for a book and then implement various functions to add, list, search, and delete books from our virtual library.
Understanding the Foundation: Data Structures
The first step in building any data-centric application is to define how our data will be represented. For a library system, the primary entity is a "Book." We need to store several pieces of information for each book. In C, we achieve this using a struct.
The Book Structure and Global Declarations
Our Book structure will contain fields for ISBN, title, author, and publication year. We'll also define some constants for maximum string lengths and the total number of books our library can hold. A global array of Book structures and a counter will keep track of our collection.
#include <stdio.h> // For standard input/output functions (printf, scanf, fgets)
#include <string.h> // For string manipulation functions (strcpy, strcmp, strcspn)
// #include <strings.h> // For strcasecmp (on POSIX systems like Linux/macOS)
#define MAX_BOOKS 100 // Maximum number of books the library can hold
#define MAX_TITLE_LEN 100 // Maximum length for book titles
#define MAX_AUTHOR_LEN 50 // Maximum length for author names
#define MAX_ISBN_LEN 15 // Maximum length for ISBNs
// Structure to represent a book
struct Book {
char isbn[MAX_ISBN_LEN];
char title[MAX_TITLE_LEN];
char author[MAX_AUTHOR_LEN];
int publicationYear;
};
// Global array to store books and a counter for current books
struct Book library[MAX_BOOKS];
int numBooks = 0; // Current number of books in the library
Here, MAX_BOOKS, MAX_TITLE_LEN, etc., are preprocessor macros defining constants. The struct Book bundles related data together. The library array will hold all our books, and numBooks tracks how many slots in the array are currently occupied. Note the use of character arrays (e.g., char isbn[MAX_ISBN_LEN];) for strings, as is common in C.
Core Functionalities: Managing Books
With our data structure defined, let's implement the essential operations for our library system: adding new books, listing all books, searching for specific books, and deleting books.
1. Adding a New Book (addBook)
This function will prompt the user for book details and add a new Book entry to our library array. We'll include a check to ensure we don't exceed the MAX_BOOKS limit. Special attention is paid to input handling using fgets to read strings that may contain spaces, and getchar() to clear the input buffer after scanf() calls.
// Function to add a new book to the library
void addBook() {
if (numBooks >= MAX_BOOKS) {
printf("Error: Library is full. Cannot add more books.\n");
return;
}
printf("\n--- Add New Book ---\n");
printf("Enter ISBN: ");
scanf("%s", library[numBooks].isbn); // Read ISBN (no spaces expected)
// Consume the newline character left by the previous scanf
// This is crucial before using fgets to read strings with spaces.
getchar();
printf("Enter Title: ");
// fgets reads the string including the newline, so we remove it
fgets(library[numBooks].title, MAX_TITLE_LEN, stdin);
library[numBooks].title[strcspn(library[numBooks].title, "\n")] = 0;
printf("Enter Author: ");
fgets(library[numBooks].author, MAX_AUTHOR_LEN, stdin);
library[numBooks].author[strcspn(library[numBooks].author, "\n")] = 0;
printf("Enter Publication Year: ");
scanf("%d", &library[numBooks].publicationYear);
numBooks++; // Increment the count of books
printf("Book added successfully!\n");
}
The strcspn function is used to find the first occurrence of a newline character (\n) in the string read by fgets and replace it with a null terminator (\0), effectively removing the trailing newline.
2. Listing All Books (listAllBooks)
This function iterates through the library array and prints the details of each book currently stored.
// Function to list all books in the library
void listAllBooks() {
if (numBooks == 0) {
printf("\nLibrary is empty. No books to display.\n");
return;
}
printf("\n--- All Books in Library ---\n");
for (int i = 0; i < numBooks; i++) {
printf("Book #%d:\n", i + 1);
printf(" ISBN: %s\n", library[i].isbn);
printf(" Title: %s\n", library[i].title);
printf(" Author: %s\n", library[i].author);
printf(" Year: %d\n", library[i].publicationYear);
printf("--------------------------\n");
}
}
3. Searching for a Book (searchBook)
Users can search for a book either by its title or its ISBN. The function will take the search term and choice from the user, then scan through the library to find matching entries. For title search, we use a case-insensitive comparison (strcasecmp, common on POSIX systems; on Windows, you might use _stricmp from <string.h>).
// Function to search for a book by title or ISBN
void searchBook() {
if (numBooks == 0) {
printf("\nLibrary is empty. No books to search.\n");
return;
}
char searchTerm[MAX_TITLE_LEN]; // Can hold title or ISBN
int choice;
printf("\n--- Search Book ---\n");
printf("Search by:\n");
printf("1. Title\n");
printf("2. ISBN\n");
printf("Enter your choice: ");
scanf("%d", &choice);
getchar(); // Consume newline after scanf
printf("Enter search term: ");
fgets(searchTerm, MAX_TITLE_LEN, stdin);
searchTerm[strcspn(searchTerm, "\n")] = 0; // Remove trailing newline
int found = 0;
printf("\nSearch Results:\n");
for (int i = 0; i < numBooks; i++) {
// strcasecmp is for case-insensitive comparison (POSIX).
// For Windows, use _stricmp or implement custom logic.
if ((choice == 1 && strcasecmp(library[i].title, searchTerm) == 0) ||
(choice == 2 && strcmp(library[i].isbn, searchTerm) == 0)) {
printf("Book found (ID: #%d):\n", i + 1);
printf(" ISBN: %s\n", library[i].isbn);
printf(" Title: %s\n", library[i].title);
printf(" Author: %s\n", library[i].author);
printf(" Year: %d\n", library[i].publicationYear);
printf("--------------------------\n");
found = 1;
}
}
if (!found) {
printf("No book found matching '%s'.\n", searchTerm);
}
}
4. Deleting a Book (deleteBook)
To remove a book, we'll ask for its ISBN. If found, we'll "delete" it by shifting all subsequent books in the array one position to the left, effectively overwriting the deleted book's position, and then decrementing numBooks.
// Function to delete a book by ISBN
void deleteBook() {
if (numBooks == 0) {
printf("\nLibrary is empty. No books to delete.\n");
return;
}
char isbnToDelete[MAX_ISBN_LEN];
printf("\n--- Delete Book ---\n");
printf("Enter ISBN of the book to delete: ");
scanf("%s", isbnToDelete); // Read ISBN
int foundIndex = -1;
for (int i = 0; i < numBooks; i++) {
if (strcmp(library[i].isbn, isbnToDelete) == 0) {
foundIndex = i;
break; // Found the book
}
}
if (foundIndex != -1) {
// Shift elements to the left to fill the gap
for (int i = foundIndex; i < numBooks - 1; i++) {
library[i] = library[i+1]; // Structure assignment copies all members
}
numBooks--; // Decrement the count of books
printf("Book with ISBN '%s' deleted successfully.\n", isbnToDelete);
} else {
printf("Book with ISBN '%s' not found.\n", isbnToDelete);
}
}
The assignment library[i] = library[i+1]; performs a member-wise copy of the struct Book, which is a convenient way to shift data in C.
Bringing It All Together: The main Function
The main function serves as the entry point and orchestrator of our library system. It will display a menu to the user and call the appropriate functions based on their choice, running in a loop until the user decides to exit.
int main() {
int choice;
do {
printf("\n--- Library Management System ---\n");
printf("1. Add Book\n");
printf("2. List All Books\n");
printf("3. Search Book\n");
printf("4. Delete Book\n");
printf("5. Exit\n");
printf("Enter your choice: ");
scanf("%d", &choice);
// No getchar() needed here if the next input is another scanf("%d")
// If case 1, 3, 4 lead to scanf then fgets, their specific getchar() calls handle it.
switch (choice) {
case 1:
addBook();
break;
case 2:
listAllBooks();
break;
case 3:
searchBook();
break;
case 4:
deleteBook();
break;
case 5:
printf("Exiting Library System. Goodbye!\n");
break;
default:
printf("Invalid choice. Please try again.\n");
}
} while (choice != 5); // Loop until user chooses to exit
return 0; // Indicate successful execution
}
The do-while loop ensures the menu is displayed at least once, and the switch statement handles the user's input, directing the program flow to the chosen function.
Complete Source Code
Here's the full code for our mini-library system. You can compile this with a C compiler (like GCC) and run it to interact with your very own console-based library!
#include <stdio.h> // For standard input/output functions (printf, scanf, fgets)
#include <string.h> // For string manipulation functions (strcpy, strcmp, strcspn)
// On POSIX systems (Linux, macOS), for case-insensitive string comparison:
// #include <strings.h>
// On Windows, use _stricmp from <string.h> instead of strcasecmp.
#define MAX_BOOKS 100 // Maximum number of books the library can hold
#define MAX_TITLE_LEN 100 // Maximum length for book titles
#define MAX_AUTHOR_LEN 50 // Maximum length for author names
#define MAX_ISBN_LEN 15 // Maximum length for ISBNs
// Structure to represent a book
struct Book {
char isbn[MAX_ISBN_LEN];
char title[MAX_TITLE_LEN];
char author[MAX_AUTHOR_LEN];
int publicationYear;
};
// Global array to store books and a counter for current books
struct Book library[MAX_BOOKS];
int numBooks = 0; // Current number of books in the library
// Function to add a new book to the library
void addBook() {
if (numBooks >= MAX_BOOKS) {
printf("Error: Library is full. Cannot add more books.\n");
return;
}
printf("\n--- Add New Book ---\n");
printf("Enter ISBN: ");
scanf("%s", library[numBooks].isbn);
getchar(); // Consume the newline character left by the previous scanf
printf("Enter Title: ");
fgets(library[numBooks].title, MAX_TITLE_LEN, stdin);
library[numBooks].title[strcspn(library[numBooks].title, "\n")] = 0;
printf("Enter Author: ");
fgets(library[numBooks].author, MAX_AUTHOR_LEN, stdin);
library[numBooks].author[strcspn(library[numBooks].author, "\n")] = 0;
printf("Enter Publication Year: ");
scanf("%d", &library[numBooks].publicationYear);
numBooks++;
printf("Book added successfully!\n");
}
// Function to list all books in the library
void listAllBooks() {
if (numBooks == 0) {
printf("\nLibrary is empty. No books to display.\n");
return;
}
printf("\n--- All Books in Library ---\n");
for (int i = 0; i < numBooks; i++) {
printf("Book #%d:\n", i + 1);
printf(" ISBN: %s\n", library[i].isbn);
printf(" Title: %s\n", library[i].title);
printf(" Author: %s\n", library[i].author);
printf(" Year: %d\n", library[i].publicationYear);
printf("--------------------------\n");
}
}
// Function to search for a book by title or ISBN
void searchBook() {
if (numBooks == 0) {
printf("\nLibrary is empty. No books to search.\n");
return;
}
char searchTerm[MAX_TITLE_LEN];
int choice;
printf("\n--- Search Book ---\n");
printf("Search by:\n");
printf("1. Title\n");
printf("2. ISBN\n");
printf("Enter your choice: ");
scanf("%d", &choice);
getchar(); // Consume newline after scanf
printf("Enter search term: ");
fgets(searchTerm, MAX_TITLE_LEN, stdin);
searchTerm[strcspn(searchTerm, "\n")] = 0;
int found = 0;
printf("\nSearch Results:\n");
for (int i = 0; i < numBooks; i++) {
// Use strcasecmp for POSIX systems, _stricmp for Windows, or custom logic.
// For simplicity, we assume strcasecmp is available or replaced.
if ((choice == 1 && strcasecmp(library[i].title, searchTerm) == 0) ||
(choice == 2 && strcmp(library[i].isbn, searchTerm) == 0)) {
printf("Book found (ID: #%d):\n", i + 1);
printf(" ISBN: %s\n", library[i].isbn);
printf(" Title: %s\n", library[i].title);
printf(" Author: %s\n", library[i].author);
printf(" Year: %d\n", library[i].publicationYear);
printf("--------------------------\n");
found = 1;
}
}
if (!found) {
printf("No book found matching '%s'.\n", searchTerm);
}
}
// Function to delete a book by ISBN
void deleteBook() {
if (numBooks == 0) {
printf("\nLibrary is empty. No books to delete.\n");
return;
}
char isbnToDelete[MAX_ISBN_LEN];
printf("\n--- Delete Book ---\n");
printf("Enter ISBN of the book to delete: ");
scanf("%s", isbnToDelete);
int foundIndex = -1;
for (int i = 0; i < numBooks; i++) {
if (strcmp(library[i].isbn, isbnToDelete) == 0) {
foundIndex = i;
break;
}
}
if (foundIndex != -1) {
// Shift elements to the left to fill the gap
for (int i = foundIndex; i < numBooks - 1; i++) {
library[i] = library[i+1];
}
numBooks--;
printf("Book with ISBN '%s' deleted successfully.\n", isbnToDelete);
} else {
printf("Book with ISBN '%s' not found.\n", isbnToDelete);
}
}
int main() {
int choice;
do {
printf("\n--- Library Management System ---\n");
printf("1. Add Book\n");
printf("2. List All Books\n");
printf("3. Search Book\n");
printf("4. Delete Book\n");
printf("5. Exit\n");
printf("Enter your choice: ");
scanf("%d", &choice);
switch (choice) {
case 1:
addBook();
break;
case 2:
listAllBooks();
break;
case 3:
searchBook();
break;
case 4:
deleteBook();
break;
case 5:
printf("Exiting Library System. Goodbye!\n");
break;
default:
printf("Invalid choice. Please try again.\n");
}
} while (choice != 5);
return 0;
}
Beyond the Basics: Enhancements and Future Steps
This mini-library system provides a solid foundation. Here are some ideas for how you could expand and improve it:
- File I/O for Persistence: Currently, all data is lost when the program exits. Implement saving the
libraryarray to a text or binary file and loading it back when the program starts. - Dynamic Memory Allocation: Instead of a fixed
MAX_BOOKSarray, usemallocandreallocto dynamically allocate memory for books, allowing the library to grow indefinitely. - Input Validation: Add more robust checks for user input (e.g., ensure the publication year is a valid number, ISBN follows a specific format).
- Borrowing/Returning System: Introduce a
struct Memberand functions to track which member has borrowed which book, along with due dates. - Sorting: Implement functions to sort the books by title, author, or ISBN.
- More Search Options: Allow searching by author, publication year range, or a combination of criteria.
- User Accounts: If you're ambitious, add a basic user authentication system.
Conclusion
Building this mini-library system has been a fantastic way to apply core C programming concepts. You've learned how to define and use structs, manage collections of data with arrays, create modular functions, and build a simple interactive command-line interface. This project highlights the power of C for structured data management and is a fundamental step towards more complex software development. Keep experimenting and happy coding!