0

I am working on code for s blood donation system. I'm currently struggling with the delete donor record function.

The delete record function is to delete the record of a donor with a given name. If there are two or more records with the same name in the file then the program asks for mobile number. While there may be more than one person with the same name, each person has a unique mobile number.

My problem is that when the same name is used for several records, the wrong record is deleted.

If there's only one record with that name, the program deletes the record in the manner that's required.

(The variable i is declared globally as int i)

Here's the delete function

void delete(struct blood *b,int n)
{
    char name[50];
    int phone;
    int found=0;
    int c=0;
    FILE *fp = fopen("bloodrecord.txt", "r");
    FILE *fp1 = fopen("temp.txt", "w");
    printf("\nEnter Name: ");
    scanf("%s", name);
    printf("---------------------------------------------\n");
    while(fread(&b[i],sizeof(struct blood),1,fp))
    {
        if(strcmpi(b[i].name,name)==0) 
        {
            c=c+1;
            printf("\nName: %s\n",b[i].name);
            printf("Age: %d\n", b[i].age);
            printf("Mobile no.: %d\n", b[i].phone);
            printf("Blood group: %s\n", b[i].bg );
            printf("Weight: %d\n", b[i].weight);
            printf("Sex: %s\n",b[i].sex);
            printf("Address: %s\n",b[i].add);
            printf("\n");
            if (c==1)
            {      
                found=1; 
            }
            else if(c>1)
            {       
                printf("\nThere are more than one occurences of this name in the records\n");                    
                printf("\nPlease enter the mobile number of the donor: ");
                scanf("%d", &phone);        
                if (b[i].phone == phone)
                {
                    found=1;
                }                   
            }
        }     
        else
        fwrite(&b[i],sizeof(struct blood),1,fp1);
    }

    fclose(fp);
    fclose(fp1);

    if (found==1)
    {
        fp1 = fopen("temp.txt", "r");
        fp = fopen("bloodrecord.txt", "w");
        
        while(fread(&b[i],sizeof(struct blood),1,fp1))
        {
            fwrite(&b[i],sizeof(struct blood),1,fp);
        }
        fclose(fp);
        fclose(fp1);
    }
    else
    {
        printf("\n\aRECORD DOES NOT EXIST.\n");
    }
    printf("RECORD SUCCESSFULLY DELETED");
    
    getchar();
    getchar();   
}
  • 1
    I think you need to do this in multiple steps: First find *and count* the number of "hits". Then in a second step you find and erase depending on conditions. – Some programmer dude Jul 21 '21 at 13:43
  • 3
    `scanf(" %[^\n]s", name);` is as [dangerous as `gets`](https://stackoverflow.com/questions/1694036/why-is-the-gets-function-so-dangerous-that-it-should-not-be-used), because you can easily read more data than your buffer can hold. Also know that `%[]` and `%s` are two separate conversion specifiers, you don't combine them. Always specify a maximum length `%49[^\n]`, which is the length of your buffer - 1. (Consider using more robust methods involving [`fgets`](https://en.cppreference.com/w/c/io/fgets)). – Oka Jul 21 '21 at 13:55
  • Are these text files (the .txt extensions suggests this)? If so, then `fwrite` is not appropriate. – Jabberwocky Jul 21 '21 at 14:26
  • Aditya Ranjan, who or what text suggested `scanf(" %[^\n]s", name);`? – chux - Reinstate Monica Jul 21 '21 at 14:51
  • @Aditya Ranjan - What is passed as the first argument of the function. what is the definition of `i` and its value at time of call? – Armali Jul 21 '21 at 16:39

2 Answers2

1

I suggest that to make the program simpler, you request both the donor's name and the donor's mobile number at the beginning.

Then you process the input file and look for both name and mobile number in a single pass.

I started with your code and made a few changes. See comments.

Please note that I have not tested this code nor compiled it. It should be essentially correct however there may be a compiler error if I made a syntax mistake.

I assume that the you are using the struct blood correctly in your code since you did not provide the code defining that struct.

I assume that an int is sufficiently large to hold the mobile number. Since the size of an int can vary and is determined by the compiler, it may or may not be large enough for a mobile number. See Range of values in C Int and Long 32 - 64 bits

One thing I do not understand is why you are using the b[i] syntax and where is the variable i defined? You could instead use a local variable in the delete() function.

I also have the delete() function returning a value indicating if it found a match or not. This may or may not be useful.

int delete()
{
    struct blood b;
    char name[50] = {0};
    int phone;
    int found = 0;
    FILE *fp = fopen("bloodrecord.txt", "r");
    FILE *fp1 = fopen("temp.txt", "w");

    // Ask for the donor's mobile number along with their name
    // at the beginning to make the search easier and be able to
    // do this in a single pass.
    printf("\nEnter Name of the donor: ");
    scanf("%49s", name);     // Oka's comments about scanf().
    printf("\nPlease enter the mobile number of the donor: ");
    scanf("%d", &phone);        
    printf("---------------------------------------------\n");

    while(fread(&b, sizeof(struct blood), 1, fp))
    {
        // check both donor's name and donor's mobile number.
        if(strcmpi(b.name, name) == 0 && b.phone == phone) 
        {
            // print out the donor data and indicate we are deleting
            // this donor record.
            printf("Deleting donor record\n");
            printf("  Name: %s\n", b.name);
            printf("  Age: %d\n", b.age);
            printf("  Mobile no.: %d\n", b.phone);
            printf("  Blood group: %s\n", b.bg );
            printf("  Weight: %d\n", b.weight);
            printf("  Sex: %s\n", b.sex);
            printf("  Address: %s\n", b.add);
            printf("\n");
            found = 1;
        }     
        else {
            // we are keeping this donor record so write it to the
            // temp file.
            fwrite(&b, sizeof(struct blood), 1, fp1);
        }
    }

    fclose(fp);
    fclose(fp1);

    if (found == 1)
    {
        // file temp.txt has deleted donors so lets updated
        // the original file, bloodrecord.txt, with the updated
        // list of donors.
        fp1 = fopen("temp.txt", "r");
        fp = fopen("bloodrecord.txt", "w");
        
        while(fread(&b, sizeof(struct blood), 1, fp1))
        {
            fwrite(&b, sizeof(struct blood), 1, fp);
        }
        fclose(fp);
        fclose(fp1);
        printf("RECORD SUCCESSFULLY DELETED");
    }
    else
    {
        printf("\n\aRECORD DOES NOT EXIST.\n");
    }
    
    getchar();
    getchar();

    return found;  // indicate if we found a match or not.
}
Richard Chambers
  • 16,643
  • 4
  • 81
  • 106
  • 1
    Aside: Instead of `fread(&b, sizeof(struct blood), 1, fp1)` and others, consider `fread(&b, sizeof b, 1, fp1)`. With `sizeof(struct blood)`, one needs to look around code to see if the correct type was used. Should the type of `b` change, more work needed to update code. Both issues fixed with `sizeof b`. – chux - Reinstate Monica Jul 22 '21 at 16:44
  • @chux-ReinstateMonica that is an excellent point. Thank you. – Richard Chambers Jul 23 '21 at 02:33
0

I reused most of your code, and added a second pass to handle the actual delete (first pass searches for matching records).

void delete(struct blood *b,int n)
    {
        const int MOBILE_SIZE = 16;
        char name[50];
        int phone = 0;
        int found=0;
        int c=0;
        FILE *fp = fopen("bloodrecord.txt", "r");
        FILE *fp1 = fopen("temp.txt", "w");
        printf("\nEnter Name: ");
        scanf("%s", name);
        printf("---------------------------------------------\n");
        while(fread(&b[i],sizeof(struct blood),1,fp))
        {
            if(strcmpi(b[i].name,name)==0) 
            {
                c=c+1;
                printf("\nName: %s\n",b[i].name);
                printf("Age: %d\n", b[i].age);
                printf("Mobile no.: %d\n", b[i].phone);
                printf("Blood group: %s\n", b[i].bg );
                printf("Weight: %d\n", b[i].weight);
                printf("Sex: %s\n",b[i].sex);
                printf("Address: %s\n",b[i].add);
                printf("\n");
                found = 1;
            }     
        }
        /* Finished the first pass. Now, start again */
        rewind(fp)
        if (c > 1) {
            printf("There are multiple records for the name %s\n", name);
            printf("\nEnter Mobile Number: ");
            scanf("%d", &phone);
        }
        while(fread(&b[i],sizeof(struct blood),1,fp))
        {
            if((c == 1 && strcmpi(b[i].name,name)==0)
                        || (c > 1 && strcmpi(b[i].name,name) == 0 && b[i].mobile == mobile))
                continue; /* skip this record */
            }     
            else
                fwrite(&b[i],sizeof(struct blood),1,fp1);
        }
        fclose(fp);
        fclose(fp1);
    
        if (found==1)
        {
            fp1 = fopen("temp.txt", "r");
            fp = fopen("bloodrecord.txt", "w");
            
            while(fread(&b[i],sizeof(struct blood),1,fp1))
            {
                fwrite(&b[i],sizeof(struct blood),1,fp);
            }
            fclose(fp);
            fclose(fp1);
        }
        else
        {
            printf("\n\aRECORD DOES NOT EXIST.\n");
        }
        printf("RECORD SUCCESSFULLY DELETED");
        
        getchar();
        getchar();   
    }
sam noir
  • 134
  • 4
  • 1
    `scanf("%s", name);` is a very bad practice to teach others. See my comment above. At the absolute minimum you must use a *field width specifier*, `scanf("%49s", name);`, to make this code safe. Even then it is bug-prone, having to maintain two magic numbers. – Oka Jul 21 '21 at 17:51