源代码:
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <conio.h>
#include <windows.h>
#include "addon.h"
static book* archivelib;
static book* noticelib;
const int MAX_LINE_LENGTH = (255);
void fileCheck(file sourcefile) { //Checks if a file exists
if (access(sourcefile.filename, F_OK) != false) {
textcolour(RED);
fprintf(stderr, "Error: %s does not exist.\n", sourcefile.filename);
exit(EXIT_FAILURE);
};
};
size_t recordcount(file source) { //Finds how many records are in a file
source.fp = fopen(source.filename, "r");
char ch;
size_t records = 0;
fseek(source.fp, START_OF_LINE, SEEK_SET);
while ((ch = fgetc(source.fp)) != EOF)
if (ch == '\n')
records++;
fclose(source.fp);
return records;
};
void textcolour(int colour) {
static int __BACKGROUND;
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
GetConsoleScreenBufferInfo(h, &csbiInfo);
SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE),
colour + (__BACKGROUND << 4));
}
void cls(HANDLE hConsole) {
CONSOLE_SCREEN_BUFFER_INFO csbi;
SMALL_RECT scrollRect;
COORD scrollTarget;
CHAR_INFO fill;
// Get the number of character cells in the current buffer.
if (!GetConsoleScreenBufferInfo(hConsole, &csbi)) {
return;
}
// Scroll the rectangle of the entire buffer.
scrollRect.Left = 0;
scrollRect.Top = 0;
scrollRect.Right = csbi.dwSize.X;
scrollRect.Bottom = csbi.dwSize.Y;
// Scroll it upwards off the top of the buffer with a magnitude of the entire height.
scrollTarget.X = 0;
scrollTarget.Y = (SHORT)(0 - csbi.dwSize.Y);
// Fill with empty spaces with the buffer's default text attribute.
fill.Char.UnicodeChar = TEXT(' ');
fill.Attributes = csbi.wAttributes;
// Do the scroll
ScrollConsoleScreenBuffer(hConsole, &scrollRect, NULL, scrollTarget, &fill);
// Move the cursor to the top left corner too.
csbi.dwCursorPosition.X = 0;
csbi.dwCursorPosition.Y = 0;
SetConsoleCursorPosition(hConsole, csbi.dwCursorPosition);
}
void clearscreen() {
HANDLE hStdout;
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
cls(hStdout);
}
const book emptybook = {
.checkdigit = (0),
.ISBN = "empty",
.title = "empty",
.DOR = "empty",
.PersonDetails = { .firstname = "empty", .lastname = "empty", .fullname = "empty"},
.status = "empty",
};
static file notice = {
.filename = "NOTICE.txt",
.sizeofline = MAX_LINE_LENGTH,
.field = { .format = "%17[^|]|%55s|%10s|%10s|%21s|%10[^|]|%26[^\n]\n", .num = (7)},
};
static file archive = {
.filename = "ARCHIVE.txt",
.sizeofline = MAX_LINE_LENGTH,
.field = { .format = "%17[^|]|%55s|%10s|%10s|%21s|%10[^|]|%26[^\n]\n", .num = (7)},
};
void printBook(book bk) { //Print information on book bk
if (strcmp(bk.title, bk.PersonDetails.firstname) == 0) {
textcolour(RED);
fprintf(stderr, "Error: The book could not be printed\n");
sleep(2);
clearscreen();
return;
};
printf("|ISBN: %s\n", bk.ISBN);
printf("|Book: %s\n", bk.title);
printf("|Authour: %s\n", bk.PersonDetails.fullname);
printf("|Date of Release: %s\n", bk.DOR);
};
book* alloclib(file libfile) {
fileCheck(libfile); //check the file
libfile.records = recordcount(libfile); //find the number of records in file
book* lib = malloc(libfile.records * sizeof *lib);
if (lib == NULL) {
textcolour(RED);
fprintf(stderr, "Memory allocation failed!\n");
exit(EXIT_FAILURE);
};
return(lib);
}
int CmpBookRecs(const void* recA, const void* recB) { //Compare book records alphabetically
int result = 0;
book* bkRecA = (book*)recA;
book* bkRecB = (book*)recB;
//Compare the titles
result = strcmp(bkRecA->title,
bkRecB->title);
//If (titles match) compare the names
if (!result)
result = strcmp(bkRecA->PersonDetails.lastname,
bkRecB->PersonDetails.lastname);
//If (names match) compare the ISBN
if (!result)
result = strcmp(bkRecA->ISBN,
bkRecB->ISBN);
return (result); //Return the the result
};
book* loadlibrary(file libsource) { //Loads all the books in "libarchive"
printf("Loading library...\n");
sleep(2);
libsource.fp = fopen(libsource.filename, "r");
if (libsource.fp == NULL) {
textcolour(RED);
fprintf(stderr, "Error opening file.\n"); //Return an error if the file cannot be opened
exit(EXIT_FAILURE);
};
int read = 0; //read will be used to ensure each line/record is read correctly
int records = 0; //records will keep track of the number of Student records read from the file
char* token; //smaller char* broken from line
const char* del = "-"; //the delimiter the line is broken into a series of tokens by
//Read all records from the file and store them into the lib
book* lib = alloclib(libsource);
do {
//Check how many fields are in the file
read = fscanf(libsource.fp, libsource.field.format, lib[records].ISBN,
lib[records].title,
lib[records].PersonDetails.firstname,
lib[records].PersonDetails.lastname,
lib[records].PersonDetails.fullname,
lib[records].DOR);
//If fscanf read values from the file, assign values then successfully add an another record
if (read == libsource.field.num)
records++;
//If read is not equal the field size and it is not the end of file
if (read != libsource.field.num && !feof(libsource.fp)) {
textcolour(RED);
fprintf(stderr, "File format incorrect. %d fields read instead of %d\n", read, libsource.field.num);
sleep(2);
exit(EXIT_FAILURE);
};
//If there was an error reading from the file exit with an error message and status
if (ferror(libsource.fp)) {
textcolour(RED);
fprintf(stderr, "Error in reading file.\n");
exit(EXIT_FAILURE);
};
lib[records].checkdigit = atoi(strrchr(lib[records].ISBN, '-'));
} while (!feof(libsource.fp) && records < libsource.records);
textcolour(LIGHTGREEN);
printf("Sorting Library...\n");
sleep(2);
qsort(lib, records, sizeof(*lib), CmpBookRecs);
textcolour(GREEN);
printf("Library was sorted!\n\n");
//free(line);
fclose(libsource.fp);
printf("Library was successfully loaded...\n");
sleep(2);
return (lib);
};
int CheckISBN(char* ISBN) { //Calculates the check digit for 13 digit International Standard Book Number
int x[13];
int count = 0;
int i = 0;
while (count < strlen(ISBN) && ISBN[i] != '\0') {
if (isdigit(ISBN[i])) {
x[count] = ISBN[i] - '0'; // Convert character to integer
count++;
} i++;
}
int s = 0,
t = 0;
//Calculation
for (i = 0; i < 12; ++i) {
if (i % 2 == 0) {
t += (x[i] * 3);
} else {
t += x[i];
} s += t;
};
int r = s % 10;
return (10 - r);
};
book booksearch(file libsource, book* lib) { //Allows the user to search for a specify book based on its title or ISBN
book foundbook; //book to be found
char* search = malloc((MAX_LINE_LENGTH + 1) * sizeof *search); //search title
char information[MAX_LINE_LENGTH]; //book title in record
char* option;
bool flag = true;
do { //Infinite loop until input
printf("\n");
textcolour(BLUE);
printf(" | _____ ___ ____ ____ __ __ __ |\n");
printf(" | / ___/ / _] / || ` / ]| | | |\n");
printf(" | ( |_ / [_ | o || D ) / / | | | |\n");
printf(" | |__ || _]| || / / / | _ | |\n");
printf(" | / ` || [_ | _ || ` / |_ | | | |\n");
printf(" | ` || || | || . `` || | | |\n");
printf(" | |___||_____||__|__||__||_| |____||__|__| |\n");
printf(" | ___ ____ ______ ____ ___ ____ _____ |\n");
printf(" | / ` | ` | || | / ` | ` / ___/ |\n");
printf(" | | || o )| | | | | || _ |( |_ |\n");
printf(" | | O || _/ |_| |_| | | | O || | | |__ | |\n");
printf(" | | || | | | | | | || | | / ` | |\n");
printf(" | | || | | | | | | || | | ` | |\n");
printf(" | |___/ |__| |__| |____| |___/ |__|__| |___| |\n");
printf(" | |\n");
printf(" |- [");
textcolour(LIGHTBLUE);
printf("1");
textcolour(BLUE);
printf("] ISBN |\n");
printf(" |- [");
textcolour(LIGHTBLUE);
printf("2");
textcolour(BLUE);
printf("] Title |\n");
printf(" |- [");
textcolour(LIGHTBLUE);
printf("3");
textcolour(BLUE);
printf("] Exit |\n\n");
//User option
switch (getc(stdin)) {
case '1': option = "ISBN";
flag = false;
break;
case '2': option = "Title";
flag = false;
break;
case '3': return(emptybook); //Exit
break;
default : textcolour(RED); //Red
fprintf(stderr, "\nInvalid option. Try again.");
clearscreen();
break;
};
} while (flag == true);
//Prompt user for the book they want to search
textcolour(YELLOW);
clearscreen();
sleep(2);
printf("\nPlease enter the %s of the book you are searching for:\n", option);
scanf("%s", search);
//fgets(search, strlen(search), stdin);
//Remove newline character if exists
if ((strlen(search) > 0) && (search[strlen(search) - 1] == '\n'))
search[strlen(search) - 1] = '\0';
//Check if title is proper
if (strcmp(option, "Title") != 0) {
//Convert to search to all caps
for (int i = 0; search[i] != '\0'; i++) {
//If any character in the char* is not a character or char* is empty
if (isalpha(search[i]) == 0 || search == NULL)
goto impromperformat;
search[i] = toupper(search[i]);
};
};
//Check if ISBN is correct
if (strcmp(option, "ISBN") == 0) {
int q[5];
int read = sscanf(search, "%3d-%2d-%4d-%3d-%1d", q[0],q[1],q[2],q[3],q[4]);
if (read != 5) {
impromperformat:
textcolour(RED);
fprintf(stderr, "This not a %s. Returning...\n", option);
return(emptybook);
}
if (read == 5 && q[4] != CheckISBN(search)) {
textcolour(RED);
fprintf(stderr, "This not a proper ISBN. Returning...\n", option);
return(emptybook);
}
};
int records = 0; //the number of lines in the record
bool found = false; //if the the book was found
int linenumber = 0;
const char* del = "|"; //character that seperates each field
//While there are book records that have not been read
while (fgets(information, libsource.sizeofline, libsource.fp) != NULL && linenumber != notice.records) {
for (int i = 0; information[i] != '\n'; i++)
information[i] = toupper(information[i]);
//If the an occurrence of the substring, (search) in the string, (line) is found
if (strstr(information, search) != NULL) {
int read = sscanf(information, libsource.field.format, lib[records].ISBN,
lib[records].title,
lib[records].PersonDetails.firstname,
lib[records].PersonDetails.lastname,
lib[records].PersonDetails.fullname,
lib[records].DOR);
if (read != 5)
goto notfound;
foundbook.checkdigit = atoi(strrchr(foundbook.ISBN, '-'));
if (foundbook.checkdigit != CheckISBN(foundbook.ISBN))
goto notfound;
if (strcmp(foundbook.status, "Available") != 0) {
textcolour(RED);
fprintf(stderr, "This book is currently unavailable.\n");
return(emptybook);
};
textcolour(LIGHTGREEN);
printf("A match was found for %s!\n", search); //Book is found
found = true;
} linenumber++;
};
notfound:
//free(token); //Free up book title
free(search); //Free up search
if (found == false) {
textcolour(LIGHTRED);
printf("Sorry, %s is currently not available. Better add one to the bookshelf!\n", search);
sleep(2);
return(emptybook); //return an empty book if found is still false
};
};
void titleview(book* lib, size_t libsize) {
lib = alloclib(notice);
lib = loadlibrary(notice);
textcolour(CYAN);
printf("| These are all records of the book currently at the library: \n");
for (int records = 0; records < libsize; records++) {
while (lib[records].checkdigit != CheckISBN(lib[records].ISBN))
records++; //Go to next record skipping the incorrect record
textcolour(BLUE);
printf("%d) ", records);
textcolour(CYAN);
if (records % 2 == 0)
textcolour(LIGHTBLUE);
printBook(lib[records]);
sleep(2);
printf("\n");
};
};
void menu(void) { //Program Menu
book found;
do { //Infinite loop until input
printf("\n");
textcolour(YELLOW);
printf(" ___________________________________________________________________________________________________________________________________\n");
printf(" |- __ ___ __ __ _____ _ __ -|\n");
printf(" |- / |/ / ___ ___ _ ___/ / ___ _ __ _ __ ___ _ / / ___ / ___/ ___ __ _ __ _ __ __ ___ (_) / /_ __ __ -|\n");
printf(" |- / /|_/ / / -_)/ _ `// _ / / _ `| |/|/ /| |/ // _ `/ / / / -_) / /__ / _ ` / ' ` / ' `/ // / / _ ` / / / __/ / // / -|\n");
printf(" |- /_/ /_/ |__/ |_,_/ |_,_/ |___/|__,__/ |___/ |_,_/ /_/ |__/ |___/ |___//_/_/_//_/_/_/|_,_/ /_//_//_/ |__/ |_, / -|\n");
printf(" |- /___/ -|\n");
printf(" |- __ _ __ ____ __ -|\n");
printf(" |- / / (_) / / ____ ___ _ ____ __ __ / __/ __ __ ___ / /_ ___ __ _ -|\n");
printf(" |- / /__ / / / _ ` / __// _ `/ / __/ / // / _` ` / // / (_-</ __// -_) / ' ` -|\n");
printf(" |- /____//_/ /_.__//_/ |_,_/ /_/ |_, / /___/ |_, / /___/|__/ |__/ /_/_/_/ -|\n");
printf(" |- /___/ /___/ -|\n");
printf(" |- -|\n");
printf(" | [1] View Upcoming Titles |\n");
printf(" | [2] Book Search |\n");
printf(" | [3] Exit |\n");
//User option
switch (getc(stdin)) {
case '1': clearscreen();
sleep(2);
titleview(noticelib, notice.records);
break;
case '2': clearscreen();
sleep(2);
found = booksearch(archive, archivelib); //Allows user to search a book
printBook(found);
break;
case '3': exit(0); //Exit
break;
default : textcolour(RED);
fprintf(stderr, "Invalid option. Try again.");
sleep(2);
clearscreen();
};
} while(1);
};
int main() {
noticelib = alloclib(notice);
archivelib = alloclib(archive);
menu();
free(archivelib); //free the archivelib
free(noticelib); //free the noticelib
return 0;
};
还有我的自定义标题:
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <conio.h>
#include <windows.h>
#include <stdio.h>
#ifndef __ADDON_H__
#define __ADDON_H__
#define MAX_PATH_LENGTH (255)
const int FIRST_LINE = 1;
const int START_OF_LINE = 0;
typedef struct {
size_t records;
size_t sizeofline;
char* filename;
FILE* fp;
struct {
int num;
char* format;
} field;
} file;
typedef struct {
int checkdigit; //permits mathematical checking for accuracy
char ISBN[18]; //ISBN number
char title[76]; //Title of material
char DOR[11]; //Date of release for the item
struct {
char firstname[11]; //first name
char lastname[11]; //last name
char fullname[22]; //First + Middle + Last name of Authour
} PersonDetails; //details about a person
char status[27];
} book; //structure for a book
enum {
BLACK = 0,
BLUE,
GREEN,
CYAN,
RED,
MAGENTA,
BROWN,
LIGHTGRAY,
DARKGRAY,
LIGHTBLUE,
LIGHTGREEN,
LIGHTCYAN,
LIGHTRED,
LIGHTMAGENTA,
YELLOW,
WHITE
};
#endif //__ADDON_H__
我一直在尝试获取在函数 loadlibrary 中读取的文件中可以成功扫描的字段数。然而,它只读取了预期 7 个字段中的两个字段。
欢迎对如何修复错误或改进我的代码进行任何更正。谢谢!
这是我目前正在测试的数据: 存档.txt
ISBN|Title|First Name|Last Name|Full Name|DOR|Status
978-51-7947-772-3|"Bleed Magic"|Orville|Baker|Orville Baker|15/04/2014|Available
978-05-0191-800-4|"Solar Waltz"|Alejandra|Bauer|Alejandra Bauer|30/07/1985|Unavailable - Rented
978-43-8687-725-8|"From Halsey to Hal's Island"|Jacklyn|Bruce|Jacklyn Bruce|09/05/1996|Available
978-36-2955-426-0|"Witch With Honor"|Miriam|Bryant|Miriam Bryant|14/07/1992|Unavailable - Damaged
978-95-2736-473-4|"Point A to Z"|Sang|Cherry|Sang Cherry|28/07/1987|Unavailable - Library use only
978-45-0742-523-8|"Priest named Sins"|Teddy|Decker|Teddy Decker|15/09/1989|Unavailable - Missing
978-27-1385-463-7|"Success of the Solstice"|Sandra|Duncan|Sandra Duncan|24/04/1989|Unavailable - In Process
978-31-8143-556-4|"What’s Over There?"|Leonel|Espinoza|Leonel Espinoza|25/03/2003|Available
978-95-5238-240-6|"The Amazing Adventures of Ice-Boy: Robots of Everest"|Ingrid|Guerra|Ingrid Guerra|24/06/2004|Available
978-85-7786-850-6|"One Boy And The World!"|Ezequiel|Hall|Ezequiel Hall|01/11/1994|Unavailable - Rented
978-28-5593-358-0|"Origin Of The Fog"|Nicky|Hancock|Nicky Hancock|23/08/2013|Available
978-33-2798-076-7|"Signs of the Past"|Lynwood|Hardin|Lynwood Hardin|27/02/1992|Available
978-23-8499-409-0|"Bleeding At The Past"|Renee|Johnston|Renee Johnston|07/05/1993|Available
978-32-9910-416-1|"Vision of Evil"|Jon|Kirby|Jon Kirby|16/02/2007|Unavailable - Rented
通知.txt
ISBN|Title|First Name|Last Name|Full Name|DOR|Status
978-89-8818-596-4|"London Titans: As We Still Exist"|Kieth|Kirk|Kieth Kirk|08/08/1996|Unavailable - Coming Soon
978-30-7350-796-6|"Linger Longer"|Ester|Li|Ester Li|05/07/2022|Unavailable - Coming Soon
978-87-6109-805-4|"Till You, My Wallflower, Are Blooming"|Penny|Lucero|Penny Lucero|06/08/2018|Unavailable - Coming Soon
978-44-5008-783-5|"Fin and Tail, Claw and Tooth"|Bernadine|Mckinney|Bernadine Mckinney|18/10/2023|Unavailable - Coming Soon
978-60-3893-332-9|"Yes... Maybe? No!"|Vicky|Mills|Vicky Mills|05/02/2008|Unavailable - Coming Soon
978-55-4205-071-3|"Mermaids and Sirens"|Genevieve|Ochoa|Genevieve Ochoa|01/07/1987|Unavailable - Coming Soon
978-66-8758-265-7|"O Death, Where Is Thy Sting?"|Mohamed|Oconnell|Mohamed Oconnell|16/02/2021|Unavailable - Coming Soon
978-68-4196-147-2|"Behind the Door"|Hunter|Payne|Hunter Payne|19/05/2010|Unavailable - Coming Soon
978-60-6519-219-5|"Stay Hidden"|Saul|Powers|Saul Powers|22/12/2004|Unavailable - Coming Soon
978-90-3065-807-8|"The Things that I Never Will Learn"|Eva|Robertson|Eva Robertson|18/06/2007|Unavailable - Coming Soon
978-74-8013-934-5|"Humans With Us Untold Gods"|Suzette|Rosales|Suzette Rosales|25/02/1987|Unavailable - Coming Soon
978-87-4206-938-7|"Whispers of a Ghost"|Diego|Russo|Diego Russo|03/06/1996|Unavailable - Coming Soon
978-66-2724-397-6|"When Love Lasts"|Barbra|Sanders|Barbra Sanders|29/11/2018|Unavailable - Coming Soon
978-78-4619-100-0|"Pelicans We"|Sheila|Whitehead|Sheila Whitehead|18/11/1982|Unavailable - Coming Soon
978-09-4189-874-4|"Obsidian Leviathan"|Elwood|Wise|Elwood Wise|10/05/2023|Unavailable - Coming Soon
978-20-7104-860-0|"Of Fire and Silk"|Reyes|Wolf|Reyes Wolf|27/02/2001|Unavailable - Coming Soon
你的程序对我来说失败了`loadlibrary()对我来说:
Number of fields read: 0
File format incorrect.
因为
argscheck()
是用指针 lib[records].ISBN
... lib[records].status
调用的。由于 lib
与 malloc()
一起分配,这些指针未初始化,并且会导致 vfscanf()
中的未定义行为,该指针指向“足够长以容纳输入序列和终止空字节的字符数组”。在我的系统上,这些指针恰好为 NULL,并且 vfscanf()
返回 0。我怀疑它们在您的系统上不是 NULL,这可能会导致内存损坏。
这是您应该发布的最小示例:
#include <stdio.h>
#include <stdlib.h>
#define LEN 99
#define str(s) str2(s)
#define str2(s) #s
int main() {
FILE *f = fopen("ARCHIVE.txt", "r");
char *a[1]; // uninitialized
// a[0] = malloc(LEN+1);
int read = fscanf(f, "%" str(LEN) "s, ", a[0]);
printf("%d\n", read);
fclose(f);
}
将字符串读入使用
scanf()
系列分配的数组时,始终指定最大字段宽度,否则长输入将导致缓冲区溢出。
如果您的
scanf()
支持“m”字符,那么 scanf()
可以为您分配这些数组,即 scanf("%ms, ", &a[0])
。如果您的输入不受信任,我仍然会指定最大字段宽度,以避免分配巨大的变量。顺便说一句,这将读取一个单词(即直到第一个空格),其中包括逗号,这不是您所期望的。
我还建议你将 I/O 和解析代码分开。两者都很棘手,混合起来会使事情变得更加困难。每个 I/O 调用都可能失败(永远失败;就像您读到
EOF
时一样)。 CSV 文件可能包含嵌入的换行符,因此我建议您将整个文件读入缓冲区(realloc()
根据需要使用数组来假定文件的最大大小)。