Home > Mobile >  cannot open RENAMED binary file in c after deleting a record
cannot open RENAMED binary file in c after deleting a record

Time:10-24

I am using C to work on a program that manages structs in a binary file.

I am having issues with my deleteData()function.

The method gets file name from user, validates file name, asks for record id to be deleted, then it copies the records except the one to be deleted from a file it is reading to a temp file.

The function should then close files, delete old file, and rename new file by the old file's name.

Everything appears to work except I can't open the new renamed file. I have checked the outcome in every step and it all appears to be ok.

Furthermore, when I look in my folder, I can see the renamed file and from it's byte size I can tell that a record has indeed been deleted.

But I can't open the file.

Please see my code below. I am working on a MAC, I have saved the folder that includes the code on my desktop. I am new to programming so any feedback or advise would be greatly appreciated.

void
deleteData()
{
    char studentID[10];
    char fileName5[30];
    char tmpFile[] = "tmp.bin";
    FILE *file;
    FILE *tmpf;
    int erFlag = 0;
    int foFlag = 0;
    int t;
    struct Record tmp;

    do {
        printf(" Enter file in which record is located:\n");
        scanf("0s", fileName5);
        if (!valSuf(fileName5))
            printf("Error wih file name.\n");
    } while (!valSuf(fileName5));

    file = fopen(fileName5, "rb");
    if (file == NULL)
        printf("Error opening file.");
    if (fread(&t, sizeof(int), 1, file) != 1)
        printf("error reading total");

    tmpf = fopen(tmpFile, "wb");
    if (tmpf == NULL)
        printf("Error opening temp file.");
    if (fwrite(&t, sizeof(int), 1, tmpf) != 1)
        printf("Error writing total.");

    printf("Enter student ID you want to delete:");
    scanf("0s", studentID);
    scanf("%*[^\n]");

    while (fread(&tmp, sizeof(Record), 1, file) != 0) {
        printf("%s\n", tmp.studentId);
        if (strcmp(tmp.studentId, studentID) != 0) {
            fwrite(&tmp, sizeof(Record), 1, tmpf);
            printf("written to file: %s\n", tmp.studentId);
        }
        else {
            foFlag = 1;
        }
    }

    if (foFlag == 1) {
        printf("Record not found.\n");
        fclose(tmpf);
        fclose(file);
        remove(tmpFile);
    }

    else {
        printf("in final stage.\n");
        printf("closing file:%d\n", fclose(file));
        printf("closing tmp: %d\n", fclose(tmpf));
        printf("removing old file:%d\n", remove(fileName5));
        printf("renaming file: %d\n", rename(tmpFile, fileName5));
    }
}

CodePudding user response:

Some issues ...

  1. You do scanf("0s", studentID); but you have [only] char studentID[10];
  2. Your logic is reversed. In the loop, you set foFlag to 1 if you match [and delete] a record. But, if (foFlag == 1) should be if (foFlag == 0).
  3. On POSIX systems, you shouldn't call remove before doing rename. Note that rename is designed to atomically unlink the old file and replace it with the new file. That is, observers [other processes] will see either the old contents or the new ones, but, never a partial or missing file.

You say that the resultant file has the shorter length but is unreadable. I don't see how that could be [from this code] unless you have an unusual umask value:

  1. At the shell prompt, enter the umask command to see the value. It should normally report 22. If not, do umask 22 to set.
  2. Try using ls -l to see what permissions are on the file.

UPDATE:

Thank you. 1. You are right. 2. Right again, I changed it to run a test and didn't change it back before posting. 3. I have now tried renaming FileName5 to whatever.bin, then I renamed tmpFile -> FileName5. Then I removed whatever.bin (after renaming) but the issue persists...... The permissions are as follows: rw-r--r--. sorry if this is a silly question, but is that not normal? – J. svatan.

The rw-r--r--. is normal. That is what we'd expect with a umask value of 22. That is, when doing an fopen(file,"wb"); the resultant file will allow read access to all and write access to the owner--what we want.

So, the file should be readable. Even if the ownership was messed up (e.g. unlikely, but if file is owned by root but you are user "me"), it should still allow read access because the file is "world/other" readable.

How are you testing for readability after creating the file? You could just do: fopen and then fclose.

I've refactored your program to be more diagnostic in nature. All/most returns are checked.

I made it create a file, delete some records, and show the file contents:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define sysfault(_fmt...) \
    do { \
        printf(_fmt); \
        exit(1); \
    } while (0)

struct Record {
    char studentID[10];
    int data;
};

void
xwrite(const void *buf,size_t buflen,FILE *fout)
{
    size_t xlen;

    xlen = fwrite(buf,1,buflen,fout);

    if (xlen != buflen)
        sysfault("xwrite: unable to write buf=%p buflen=%zu xlen=%zu -- %s\n",
            buf,buflen,xlen,strerror(errno));
}

int
makerec(FILE *fout,int data,const char *studentID)
{
    struct Record rec;
    size_t len = strlen(studentID);

    rec.data = data;

    if (len >= sizeof(rec.studentID))
        sysfault("makerec: studentID too long -- '%s'\n",studentID);
    strcpy(rec.studentID,studentID);

    xwrite(&rec,sizeof(rec),fout);

    return 1;
}

void
makefile(const char *file)
{

    FILE *fout = fopen(file,"wb");
    if (fout == NULL)
        sysfault("makefile: unable to open '%s' -- %s\n",file,strerror(errno));

    int t = 0;

    // write out phony count
    xwrite(&t,sizeof(t),fout);

    t  = makerec(fout,23,"123456789");
    t  = makerec(fout,37,"abcdefghi");
    t  = makerec(fout,17,"jklmnopqr");

    if (fseek(fout,0,0) != 0)
        sysfault("makefile: unable to rewind -- %s\n",strerror(errno));

    // write out real count
    xwrite(&t,sizeof(t),fout);

    fclose(fout);
}

void
showfile(const char *fileName5)
{
    FILE *fin;
    int t;
    struct Record tmp;

    fin = fopen(fileName5, "rb");
    if (fin == NULL)
        sysfault("showfile: Error opening %s -- %s\n",
            fileName5,strerror(errno));

    if (fread(&t, 1, sizeof(int), fin) != sizeof(int))
        sysfault("deleteRec: error reading total -- %s\n",strerror(errno));
    printf("showfile: count is %d\n",t);

    while (fread(&tmp, 1, sizeof(tmp), fin) != 0)
        printf("%d %s\n", tmp.data, tmp.studentID);

    fclose(fin);
}

void
deleteRec(const char *fileName5,const char *studentID)
{
    char tmpFile[1000];
    FILE *file;
    FILE *tmpf;
    int erFlag = 0;
    int foFlag = 0;
    int t;
    struct Record tmp;

    printf("\n");

    file = fopen(fileName5, "rb");
    if (file == NULL)
        sysfault("deleteRec: Error opening %s -- %s\n",
            fileName5,strerror(errno));

    if (fread(&t, 1, sizeof(int), file) != sizeof(int))
        sysfault("deleteRec: error reading total -- %s\n",strerror(errno));

    strcpy(tmpFile,fileName5);
    strcat(tmpFile,".TMP");

    tmpf = fopen(tmpFile, "wb");
    if (tmpf == NULL)
        sysfault("deleteRec: Error opening temp file -- %s\n",strerror(errno));

    xwrite(&t, sizeof(int), tmpf);

    while (fread(&tmp, 1, sizeof(tmp), file) != 0) {
        printf("deleteRec: %d %s\n", tmp.data, tmp.studentID);

        if (strcmp(tmp.studentID, studentID) != 0) {
            xwrite(&tmp, sizeof(tmp), tmpf);
        }
        else {
            printf("deleteRec: skipped %s\n", tmp.studentID);
            foFlag = 1;
        }
    }

    printf("closing file:%d\n", fclose(file));
    printf("closing tmp: %d\n", fclose(tmpf));

    if (foFlag == 0) {
        printf("Record not found.\n");
        remove(tmpFile);
    }

    else {
        printf("in final stage.\n");
        //printf("removing old file:%d\n", remove(fileName5));
        printf("renaming file: %d\n", rename(tmpFile, fileName5));
    }
}

void
deleteData(void)
{
    char studentID[1000];
    char fileName5[1000];

    printf(" Enter file in which record is located:\n");
    scanf("%s", fileName5);

    printf("Enter student ID you want to delete:");
    scanf("%s", studentID);

    deleteRec(fileName5,studentID);
}

int
main(void)
{
    const char *file = "mydata.bin";

    makefile(file);
    showfile(file);

    deleteRec(file,"abcdefghi");
    deleteRec(file,"zzzz");

    showfile(file);
}

Here is the program output:

showfile: count is 3
23 123456789
37 abcdefghi
17 jklmnopqr

deleteRec: 23 123456789
deleteRec: 37 abcdefghi
deleteRec: skipped abcdefghi
deleteRec: 17 jklmnopqr
closing file:0
closing tmp: 0
in final stage.
renaming file: 0

deleteRec: 23 123456789
deleteRec: 17 jklmnopqr
closing file:0
closing tmp: 0
Record not found.
showfile: count is 3
23 123456789
17 jklmnopqr

Note that the count remains 3 because deleteRec does not adjust it down if it finds a record to delete [exercise left for reader ;-)]

If the file is unreadable, then the final showfile will error out.

CodePudding user response:

Thank you for the assistance! I ended up being inspired by Neil's advice and redefined the function such that it reads the data into a struct array then write it(while omitting the record to be deleted) into the same file. It now works, please see below.

{
    char studentID[10];
    char fileName5[30];
    FILE *file;
    int foFlag = 0;
    int total = 0;
    struct Record *fileData5;
    int j = 0;

    do
    {
        printf(" Enter file in which record is located:\n");
        scanf("0s", fileName5);
        if (!valSuf(fileName5))
            printf("Error wih file name.\n");
    } while (!valSuf(fileName5));

    printf("Enter student ID you want to delete:");
    scanf("s", studentID);

    fileData5 = read_file(fileName5, &total);
    if (fileData5 == false)
    {
        printf("Error reading file. \n");
    }

    struct Record *copy;
    copy = malloc(sizeof(Record) * total);

    for (int i = 0; i < total; i  )
    {
        if (strcmp(fileData5[i].studentId, studentID) != 0)
        {
            copy[j] = fileData5[i];
            j  ;
            foFlag=0;
        }
        else
        {
            foFlag = 1;
        }
    }
    if (foFlag == 0)
        printf("Record not found.\n");

    else if (writeFile(fileName5, copy, j))
        printf("Record %s has been deleted successfully\n", studentID);
    else
    {
        printf("Error deleting %s.\n", studentID);
    }
    free(copy);
}```
   
  • Related