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("%30s", 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("%30s", 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));
}
}
Some issues ...
scanf("%30s", studentID);
but you have [only] char studentID[10];
foFlag
to 1 if you match [and delete] a record. But, if (foFlag == 1)
should be if (foFlag == 0)
.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:
umask
command to see the value. It should normally report 22
. If not, do umask 22
to set.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.