Ansi C: Attempting to count total chars in a file - c

The task is simple but I am having an issue with the method returning 0.
This means my loop:
int getCharCount(FILE *fp) {
int c;
int i = 0;
while( (c = fgetc(fp)) != EOF) {
i++;
printf("Loop ran");
}
return i;
}
Did not run.
In my testing I found that the loop never runs because the "Loop ran" never prints. I am new to c and not sure if I am doing something wrong when trying to count chars in the file.
I feel like I should mention that the file is opened with "wb+" mode and that there are a few long methods that edit the file. Essentially before using this getCharCount() method the text file is cleared of all previous data, then user enters a number of 44 char length strings at a time and I use this method I just posted to calculate the total number of chars which will be used to navigate my display data method.
I am in a library working on this so if anything extra is needed to be posted or if anything needs to be clarified I will try to be quick with my responses. I don't want to post my whole code because there would be a chance to cheat and I need to get this done myself.
Thanks ahead.

If you write to the file and then call your method on the same file handle, the file handle is already at the end of the file so it will see EOF immediately. We would need to see more of the code to be sure I think.
So, you could rewind the file handle at the start of your function.
Or you could just call ftell to find out your offset in the file, which is the same as the number of bytes written if you truncate, write and do not rewind.

Why you have to read all bytes one by one to count them? It is much easier to do fseek( fp, 0, 2 ) to jump at end of file and get current position (file length) with ftell( fp ).

You are opening with the mode w+ which will truncate the file. From the fopen man page:
w+ Open for reading and writing. The file is created if it does not exist, otherwise it is truncated. The stream is positioned at the beginning of the file.
You will want to open it with rb+ instead of wb+ if you are reading from a file and still want to write to it. If you have already written to it and want to read what was written, you will need to seek to the start of the file pointer.
// Both of these seek to the start
rewind(fp);
fseek(fp, 0, SEEK_SET);

If the file is not open, you could use:
off_t getFileSize(const char *filepath) {
struct stat fileStat;
stat(filepath, &fileStat);
return(fileStat.st_size);
}
If the file is open:
off_t getFileSize(int fd) {
struct stat fileStat;
fstat(fd, &fileStat);
return(fileStat.st_size);
}

Related

Can't rewrite the data at file starting but I can apped at the End in append mode in C

I want to store student information in a file in C, I choose to write in a binary file format.first I had written all student data to the file in write mode. first 4 bytes(int) represents the num of students and then subsequent data is for storing student Information.
when I want to append some more student data to the file, I can apped the student data at the end of file in append mode but could not update the no of students at the start of file (first 4 bytes(int)) which are already written in write mode. There is no where stated in manpages that I can't rewrite the data that are already written in write mode. Below is the code. Thanks in advance.
typedef struct
{
char* rollNo;
char* name;
char* grade;
}Student;
int numStudents;
void append(Student* newStudent){
FILE *fp;
if((fp=fopen("students.bin","ab"))==NULL){ f_err_report(ERR_OPEN);exit(1);}
//appending the new student at the end of the file
// writing rollNo
int len;
len = strlen(newStudent->rollNo)+1;
if(fwrite(&len,sizeof(int),1,fp)!=1) {
f_err_report(ERR_WRITE);
}
if(fwrite(newStudent->rollNo,sizeof(char)*len,1,fp)!=1) {
f_err_report(ERR_WRITE);
}
//writing name
len = strlen(newStudent->name)+1;
if(fwrite(&len,sizeof(int),1,fp)!=1) {
f_err_report(ERR_WRITE);
}
if(fwrite(newStudent->name,sizeof(char)*len,1,fp)!=1) {
f_err_report(ERR_WRITE);
}
//writing grade
len = strlen(newStudent]->grade)+1;
if(fwrite(&len,sizeof(int),1,fp)!=1) {
f_err_report(ERR_WRITE);
}
if(fwrite(newStudent->grade,sizeof(char)*len,1,fp)!=1) {
f_err_report(ERR_WRITE);
}
//updating the total number of students at the file starting. But this doesn't happen!!! It stays same as when I wrote in "write mode" previously
numStudents++;
rewind(fp);
if(fwrite(&numStudents,sizeof(int),1,fp)!=1){
f_err_report(ERR_WRITE);
}
printf("appended to the list \n");
fclose(fp);
}
When you called
rewind(fp);
you successfully reset the file pointer to the beginning of the file, ready to update the number of student records. All right so far.
But then you called
fwrite(&numStudents, sizeof(int), 1, fp)
to actually update the number of records. And since the file is opened in append mode, this translates implicitly to the equivalent of
fseek(fp, OL, SEEK_END);
fwrite(&numStudents, sizeof(int), 1, fp)
That is, in append mode, every time you write to the file, there's an automatic seek to the end of the file, followed by the write you asked for.
So your rewind() call accomplishes nothing in the long run, because it's undone by the next write.
Once upon a time, calling fopen with "a" mode basically just opened the file, and seeked to the end once. In those days, your code would have worked as you expected. But not any more. In your case, you want to use "w" mode (or "w+" or "r+", if you want to both read and write), and when you want to append a new record at the end, call
fseek(fp, OL, SEEK_END);
to explicitly get to the end when you need to.
when I want to append some more student data to the file, I can apped the student data at the end of file in append mode but could not update the no of students at the start of file (first 4 bytes(int)) which are already written in write mode. There is no where stated in manpages that I can't rewrite the data that are already written in write mode.
Yes there is:
Opening a file with append mode ('a' as the first character in the mode argument) causes all subsequent writes to the file to be forced to the then current end-of-file, regardless of intervening calls to the fseek function.
And from the Linux man page:
Opening a file in append mode (a as the first character of mode)
causes all subsequent write operations to this stream to occur at
end-of-file, as if preceded the call:
fseek(stream, 0, SEEK_END);
The only way on POSIX systems to open a file in append mode but still be able to write to any position in a file is to use the POSIX open() call with the O_APPEND flag and then use the POSIX pwrite() call to write to the desired location:
The pwrite() function shall be equivalent to write(), except that it writes into a given position and does not change the file offset (regardless of whether O_APPEND is set).
But pwrite() is broken on Linux:
POSIX requires that opening a file with the O_APPEND flag should
have no effect on the location at which pwrite() writes data.
However, on Linux, if a file is opened with O_APPEND, pwrite()
appends data to the end of the file, regardless of the value of
offset.

Reading from file in C using fread

I'm learning how to read content from a file in C. And I manage to scrape through the following code.
#include <stdio.h>
#include <stdlib.h>
void read_content(FILE *file) {
char *x = malloc(20);
// read first 20 char
int read = fread(x,sizeof(char),20,file);
if (read != 20) {
printf("Read could not happen\n");
}
else {
printf("the content read is %s",x);
}
free(x);
return;
}
int main(int argc,char *argv[]) {
FILE *fp;
fp = fopen("test.txt","w+");
read_content(fp);
fclose(fp);
return 0;
}
But for some reason (which I'm not able to understand) I see the read bytes count as 0.
The problem is that you open the file with the w+ mode. There are two possibilities:
if the file doesn't exist, it will be created empty. Reading from it immediately gives end of file resulting in fread() returning 0.
if the file does exist, it will be truncated i.e. changed into an empty file. Reading from it immediately gives end of file resulting in fread() returning 0.
If you just want to read from the file (as per your example), open it in mode r. If you want to read and write without destroying its existing content, use mode r+.
Whatever mode you choose, always check that fopen() returns non null and print the error if it returns null (this is not the cause of your problem but is best practice).
From Man Page w+ flag:
Open for reading and writing. The file is created if it does
not exist, otherwise it is truncated.
You are probably trying to open a file which doesn't exist at the path you provided, or is read-only as #WhozCraig suggested in comment. This means a new file is being created, an empty file! hence you are seeing 0 bytes read.
To sum up, The fopen is failing, in that case you need to check the return value if it is equal to -1.
To find what was the error, you can check the errno as it is set to
indicate the error.
If you are only intending to read, open the file with r flag instead of w+
The problem lies within this line of code:
fp = fopen("test.txt","w+")
the "w+" mode, clear the previous content of the file and the file will be empty when you just going to read the file without writing anything to it. Hence, it is printing "Read could not happen" because you are trying to read an empty file.
I would suggest you to use "r+" mode, if you are willing to read and then write into the file. Otherwise, r mode is good enough for simple reading of a file.

checking a file from a certain offset without reading the entire file

what I want to do is open a file(which is huge) and read from a certain point of bytes to an offset.
in c# this can be done with:
File.ReadAllBytes(file).Skip(50).Take(10).ToArray();
the problem with this is that it reads the entire file but since my files can be huges this also takes a long time. is there a way to read parts of a file similiar to this method but WITHOUT reading the entire file? In c preferably
Yes, use the fseek() standard library function to move ("seek") to the desired position:
FILE *in = fopen("myfancyfile.dat", "rb");
if(fseek(in, 50, SEEK_SET) == 0)
{
char buf[10];
if(fread(buf, sizeof buf, 1, in) == 1)
{
/* got the data, process it here */
}
}
fclose(in);

Once a file is opened using fscanf() can I read it again?

I need to open a text document and read it's data. One of the data points in this file is an int that I need to use to create a data structure.
Question: Once I open a file passed in from the command line with fscanf() can I read the file again with fscanf() or another stream reading function? I have tried do this by calling fscanf() then fgetc() but I find that the one I call first is the only one that runs.
In order to re-read all or a portion of an opened file, you need to reposition the stream.
You can use fseek() to reposition the file stream to the beginning (or other desired position):
int fseek(FILE *stream_pointer, long offset, int origin);
e.g.
FILE *file_handle;
long int file_length;
file_handle = fopen("file.bin","rb");
if (fseek(file_handle, 0, SEEK_SET))
{
puts("Error seeking to start of file");
return 1;
}

Does fseek() move the file pointer to the beginning of the file if it was opened in "a+b" mode?

I wish to open a file using the "a+b" mode, i.e. if it does not exist it is created automatically, but if it does I don't want to overwrite it. I want to be able to read and write to the file.
The file is binary, and I want to save records of a specific struct in it. So I want to do fseek() to the record I want and then save the record using fwrite().
The code looks as follows (MyRecord is a typedef to a struct, while FILENAME is a #define to the file's name):
int saveRecord(MyRecord *pRecord, int pos)
{
FILE* file = fopen(FILENAME, "a+b");
if (file == NULL)
{
printf("Unable to open file %s\n", FILENAME);
return 0;
}
fseek(file, pos * sizeof(MyRecord), SEEK_SET);
fwrite(pRecord, sizeof(MyRecord), 1, file);
fclose(file);
return 1;
}
However this code just appends the record to the end of the file, even if I set pos to 0. Why isn't fseek() with SEEK_SET working in append mode?
I know I can simply open it with "r+b" and if it fails open it with "wb", but I want to know why this doesn't work and why fseek() with SEEK_SET is leaving the file pointer at the end. Any references to places where this behaviour is documented appreciated (because I couldn't find any, or I am using the wrong keywords).
That's because in a mode, writing to the FILE* always appends to the end. fseek only sets the read pointer in this mode. This is documented in the C standard, 7.19.5.3 fopen:
Opening a file with append mode ('a' as the first character in the mode argument)
causes all subsequent writes to the file to be forced to the then current end-of-file,
regardless of intervening calls to the fseek function.
Plain C does not have any sane way to achieve what you want. If you're on a POSIX system or anything remotely close, you can use fd=open(FILENAME, O_CREAT|O_RDRW, 0666) and then fdopen(fd, "rb+").
Edit: Another thing you could try, with plain C:
f = fopen(FILENAME, "a+b");
if (!f) /* ... */
tmp = freopen(0, "r+b", f);
if (tmp) f = tmp;
else /* ... */
Use "r+b" mode and fallback to "w+b" if it fails.
The "a+b" mode, allows you to read and append; the "r+b" allows random read and write.
The documentation for fopen describes how the file behaves with the different modes.

Resources