以下程序似乎在大多数情况下都可以正常运行,但是如果我添加了最后一个库存位置的记录(记录10),则会导致问题。具体而言,如果我添加记录10,然后尝试删除它,则在调用printList()之后它仍显示在清单中。只有最终记录才是这种情况,其他任何情况都不会发生。
任何人都可以找出问题所在吗?使用gcc进行编译时,我一直在使用-Wall,并且它不会发出任何警告。我也一直在尝试找出如何使用gdb,但是我仍在学习,因此也没有用。
#include <stdio.h>
#include <string.h>
typedef struct {
unsigned int record;
char tool[30];
unsigned int quantity;
double price;
} hardware;
void menu(FILE *fPtr, hardware *toolsPtr);
void initialiseRecords(FILE * fPtr, hardware *toolsPtr);
void inputTool(FILE * fPtr, hardware *toolsPtr);
void printList(FILE * fPtr, hardware *toolsPtr);
void deleteRecord(FILE * fPtr, hardware *toolsPtr);
int main(void)
{
FILE *fPtr;
hardware tools = { 0, "", 0, 0.0 }, *toolsPtr;
toolsPtr = &tools;
if ((fPtr = fopen("hardware.dat", "wb+")) == NULL) {
puts("File cannot be opened.") ;
}
else {
menu(fPtr, toolsPtr);
}
fclose(fPtr);
}
void menu(FILE *fPtr, hardware *toolsPtr)
{
unsigned int choice;
printf("\n%s\n\n%s\n\n%s\n%s\n%s\n%s\n%s\n\n%s",
"** Hardware Inventory Program **",
" Enter a menu option:",
"1 - Initialise the record file.",
"2 - Add a record to the file.",
"3 - Delete a record from the file.",
"4 - Print the current inventory",
"5 - Quit the program.", "> ");
scanf("%u", &choice);
while (choice != 5) {
switch (choice) {
case 1:
initialiseRecords(fPtr, toolsPtr);
break;
case 2:
inputTool(fPtr, toolsPtr);
break;
case 3:
deleteRecord(fPtr, toolsPtr);
break;
case 4:
printList(fPtr, toolsPtr);
break;
default:
puts("Incorrect entry.");
break;
}
printf("\n%s\n\n%s\n\n%s\n%s\n%s\n%s\n%s\n\n%s",
"** Hardware Inventory Program **",
" Enter a menu option:",
"1 - Initialise the record file.",
"2 - Add a record to the file.",
"3 - Delete a record from the file.",
"4 - Print the current inventory",
"5 - Quit the program.", "> ");
scanf("%u", &choice);
}
}
void initialiseRecords(FILE * fPtr, hardware *toolsPtr)
{
fseek(fPtr, 0, SEEK_SET);
for (unsigned int i = 0; i < 10; ++i) {
char s[30] = "";
sscanf(s, "%s", toolsPtr->tool);
toolsPtr->record = i + 1;
toolsPtr->quantity = 0;
toolsPtr->price = 0.0;
fwrite(toolsPtr, sizeof(hardware), 1, fPtr);
}
}
void inputTool(FILE * fPtr, hardware *toolsPtr)
{
printf("\n%s\n\n%s", "Enter record # (-1 to quit):", "> ");
scanf("%u", &toolsPtr->record);
while(toolsPtr->record != -1) {
fseek(fPtr, (toolsPtr->record - 1) * sizeof(hardware), SEEK_SET);
fread(toolsPtr, sizeof(hardware), 1, fPtr);
getchar();
if (!strcmp(toolsPtr->tool, "")) {
printf("\n%s\n\n%s", "Enter tool name:", "> ");
fgets(toolsPtr->tool, 30, stdin);
toolsPtr->tool[strlen(toolsPtr->tool) - 1] = '\0';
printf("\n%s\n\n%s", "Enter quantity:", "> ");
scanf("%u", &toolsPtr->quantity);
printf("\n%s\n\n%s", "Enter price:", "> ");
scanf("%lf", &toolsPtr->price);
fseek(fPtr, (toolsPtr->record - 1) * sizeof(hardware), SEEK_SET);
fwrite(toolsPtr, sizeof(hardware), 1, fPtr);
}
else {
getchar();
puts("There is an existing record with this number.");
fseek(fPtr, 0, SEEK_SET);
}
printf("\n%s\n\n%s", "Enter record (-1 to quit):", "> ");
scanf("%u", &toolsPtr->record);
}
}
void printList(FILE * fPtr, hardware *toolsPtr)
{
fseek(fPtr, 0, SEEK_SET);
printf("\n%-10s%-30s%-10s%-10s\n\n", "Record #",
"Tool Name", "Quantity", "Price");
for (unsigned int i = 0; i < 10; ++i) {
fread(toolsPtr, sizeof(hardware), 1, fPtr);
printf("%-10u%-30s%-10u$%-10.2lf\n",
toolsPtr->record, toolsPtr->tool,
toolsPtr->quantity, toolsPtr->price);
}
}
void deleteRecord(FILE * fPtr, hardware *toolsPtr)
{
printf("\n%s\n\n%s",
"Enter the record number of the tool to delete:", "> ");
scanf("%u", &toolsPtr->record);
fseek(fPtr, (toolsPtr->record - 1) * sizeof(hardware), SEEK_SET);
fwrite(toolsPtr, sizeof(hardware), 1, fPtr);
}
答案 0 :(得分:0)
基本上,使用stdio.h函数不能真正删除任何内容。甚至没有缩略词(尽管我在unistd.h中确实有这样的功能)。因此,在使用这种类型的数据库时,您确实应该这样做:
为每个记录添加一个“已删除”字段。用它来确定应显示哪些记录。
为每个记录添加一个“ id”字段。这样,同一条记录将始终具有相同的键。
添加记录时,请查找已删除的记录以重复使用,并在创建记录时使用新的“ id”。
然后,您不必担心会截断文件。
OR
如果您可以将文件截断为指定的长度,则只需加载最后一条记录(如果不是您要删除的记录),然后将其移动到要删除或移动的位置即可。他们全都退了1条记录以保持秩序。然后,您可以将文件截断为所需大小。
此外,您使用的模式为“ wb +”,这是错误的,因为打开文件时它应该将文件截断(不截断)。您真正想要的是模式“ r + b”,可以将其打开以进行读写,但不会截断。它不会说是否不存在就创建文件,因此,如果文件尚不存在,您可能需要检查并仅使用“ w + b” 。
也,在initializeRecords中,行char s [30] =“”。我不确定是否真的允许这样做,但是最多只能初始化第一个字符(s [0] ='\ 0';)。更好(更容易)的方法是像这样初始化toolsPtr:
fseek(fPtr, 0, SEEK_SET);
for (unsigned int i = 0; i < 10; ++i) {
memset(toolsPtr, 0, sizeof(hardware));
toolsPtr->record = i + 1;
fwrite(toolsPtr, sizeof(hardware), 1, fPtr);
}
唯一真正的区别可能是文件将用零填充,而不是内存中的随机垃圾。如果您开始在文件上使用hexdump至少可以查看出现问题的情况,则可能会有所不同。