【问题标题】:Editing records in binary file C编辑二进制文件 C 中的记录
【发布时间】:2021-04-23 19:03:00
【问题描述】:

我正在开发一个关于二进制文件中记录的 CRUD 操作的 C 项目。 记录使用结构数据类型。

我在更新记录时遇到问题。这是函数:

void modifyGoods(char fileName[]) {

     struct myGood newGood;
     int idNumber, found = 0;

     printf("\nUPDATING THE RECORD...");
     printf("\nEnter the id of the record you would like to update: ");
     scanf("%d", &idNumber);

     FILE* filePointer = fopen(fileName, "rb+");

     while ((fread(&newGood, sizeof(newGood), 1, filePointer)) > 0 && found == 0) {
         if (newGood.id == idNumber) {

// new data of the record
             printf("\nEnter the new data:\n");

             printf("Category: ");
             scanf("%s", &newGood.category);

             printf("Description: ");
             scanf("%s", &newGood.description);

             printf("Price: ");
             scanf("%f", &newGood.price);

             printf("Quantity: ");
             scanf("%d", &newGood.quantity);

// go one record back in order to overwrite the record that needs to be updated
             fseek(filePointer, -(long)sizeof(newGood), SEEK_CUR);
//overwrites record
             fwrite(&newGood, sizeof(newGood), 1, filePointer);
             
             printf("Record updated!\n");
             found = 1;
             printf("%d", found);

         }
     }

     fclose(filePointer);
     if (found == 0) {
         printf("\nERROR! THIS ID DOESN'T EXIST!\n\n");
     }

}

如果我更新二进制文件的第一条记录,它会按预期工作,但如果我尝试更新第一条之后的任何记录,就会发生这种情况:

A B C - 二进制文件中的记录。

如果我尝试更新 B,则它变为:A B(更新)B(旧记录)。

记录被更新,但他之后的记录被第二个记录的旧值覆盖。

我应该如何解决这个问题?我更改了 fseek 函数的偏移量,但仍然不起作用。

谢谢, 马可

【问题讨论】:

  • 只有在 fseek() 失败时才会发生这种情况。
  • 我不知道除此之外会发生什么,但是您会以这种方式阅读两条记录。在第一个循环之后,它将首先再次读取然后停止,因为 found != 0。
  • @EmanuelP found == 0 表示继续前进,而不是停止。
  • 为你的while循环切换条件以避免额外的fread()——现在它在检查found之前执行读取,所以在found被更改之后它会额外读取一次虽然它不会再次进入循环体。不过,这可能是无害的,因为在额外读取之后您所做的只是关闭文件。

标签: c structure binaryfiles


【解决方案1】:

首先,

 scanf("%d", &idNumber);

你怎么知道scanf成功了?如果用户刚刚按下回车,您是否愿意接受 idNumber 定义时发生的任何值?

测试每一个返回码。关于唯一可以安全忽略的是关闭文件时。 (它几乎不会发生,如果发生了,你能做什么?)

我不确定您的程序出了什么问题,但我有两个建议可能会有所帮助。

  1. 在调用 fread 之前,使用 ftell(3) 记录位置,而不是返回某个固定距离。然后,当您要覆盖记录时,寻找记录的位置。这样,如果记录大小发生变化(或者您一开始就猜错了),逻辑仍然有效。

  2. 使用中断而不是布尔值来退出循环。您的教授可能会说这不是结构化代码,但您的同事不会介意。

您可以通过切换到 for 循环来添加 ftell:

size_t len;
for( off_t pos=ftell(filePointer); 
     (len = fread(&newGood, sizeof(newGood), 1, filePointer)) == sizeof(newGood; 
     pos=ftell(filePointer) ) {
  ...
  if( -1 == fseek(filePointer, pos, SEEK_SET) ) {
    err(EXIT_FAILURE, "could not seek");
  }
  if (newGood.id != idNumber) {
     continue;
  } 

  //overwrites record
  if( sizeof(newGood) != fwrite(&newGood, sizeof(newGood), 1, filePointer) ) {
    err(EXIT_FAILURE, "could not write");
  }
         
  printf("Record updated!\n");
  found = 1;
  printf("%d", found);
  break;
}

还有一点:当您退出循环时,您假设如果 found 为 0,则未找到记录。 fread(3) 可以在出错时返回 0,从而导致循环退出,在这种情况下,告诉用户记录不存在有点误导。

【讨论】:

    猜你喜欢
    • 2011-10-29
    • 2020-03-21
    • 2020-08-23
    • 2017-02-02
    • 1970-01-01
    • 2011-09-27
    • 2011-03-14
    • 2013-06-25
    • 2010-12-27
    相关资源
    最近更新 更多