Программа инвентаризации, возможная проблема граничного значения? - PullRequest
0 голосов
/ 04 февраля 2019

Представленная ниже программа, кажется, работает хорошо в большинстве ситуаций, но если я добавлю запись для последней позиции инвентаря (запись 10), это вызовет проблемы.В частности, если я добавлю запись 10, а затем попытаюсь удалить ее, она все равно будет отображаться в списке после вызова функции printList ().Это относится только к окончательной записи и не относится ни к одному из остальных.

Может кто-нибудь разобраться в чем проблема?Я использовал -Wall при использовании gcc для компиляции, и он не выдает никаких предупреждений.Я также пытался понять, как использовать 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);
}

1 Ответ

0 голосов
/ 04 февраля 2019

По сути, используя функции stdio.h, вы не можете ничего удалить.Там даже нет ftruncate (хотя у меня есть такая функция в unistd.h).Таким образом, при работе с базой данных этого типа вам действительно следует сделать следующее:

Добавить «удаленное» поле к каждой записи.Используйте его, чтобы определить, какие записи следует отображать.

Добавьте поле «id» для каждой записи.Таким образом, одна и та же запись всегда имеет один и тот же ключ.

Когда вы добавляете запись, ищите удаленную для повторного использования и используйте новый "id", когда вы делаете.

Затем выникогда не нужно беспокоиться об усечении файла.

ИЛИ

Если у вас есть способ обрезать файл до указанной длины, вы можете просто загрузить последнюю запись (если это не та запись, которую выудалить) и переместить его в положение, которое вы хотите удалить, или сдвинуть их назад на 1 запись, чтобы сохранить порядок.Затем вы можете обрезать файл до нужного размера.

Кроме того , вы используете режим "wb +", который является неправильным, так как он должен обрезать файл (до нуля), когдаоткрывая его.что вам действительно нужно, так это режим «r + b», который открывает его для чтения и записи, но не усекает.Он не говорит, создает ли он файл, если он не существует, поэтому вам может понадобиться проверить и использовать «w + b» только , если он еще не существует.

Также , в initializeRecords у вас есть строковый символ 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 для файла, чтобы посмотреть, что произошло, если что-то пойдет не так, по крайней мере ..

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...