About the FILE * streams and how fputc() works - c

I wonder about the operation of FILE pointer f and how the function fputc works.
First, when I open a file (I have not been working on it yet, like writing or reading). What position of f in the file? Is it before the first character?
Second, when I use:
fseek(f, -1, SEEK_CUR);
fputc(' ', f);
what position of my pointer f now?

Reading the manuals should help you.
For fopen: the stream is positioned at the beginning of the file. Except for mode like 'a'
For fseek: that function can fail, you have to test the return value; and it is not difficult to imagine that you cannot obtain a negative offset.

When you open the file, the current position is 0, at the first character.
If you try to fseek before the beginning of the file, fseek will fail and return -1.
Note that if you seek backwards on a text file, there is no guarantee that is can succeed. On linux and/or for a binary stream, assuming you are not at the start of the stream, opened in write mode for a real file, after the sequence
fseek(f, -1L, SEEK_CUR);
fputc(' ', f);
the position of the stream will be the same as before the fseek.
But consider this seemingly simpler example:
fputc('\n', f);
fseek(f, -1L, SEEK_CUR);
On systems such as Windows, where '\n' will at some point be converted into a sequence of 2 bytes <CR><LF>, what do you think it should do?
Because of all these possibilities for failure (and a few more exotic ones), you should always test the return value of fseek and try to minimize its use.

When accessing files through C, the first necessity is to have a way to access the files. For C File I/O you need to use a FILE pointer, which will let the program keep track of the file being accessed. For Example:
FILE *fp;
To open a file you need to use the fopen function, which returns a FILE pointer. Once you've opened a file, you can use the FILE pointer to let the compiler perform input and output functions on the file.
FILE *fopen(const char *filename, const char *mode);
Here filename is string literal which you will use to name your file and mode can have one of the following values
w - open for writing (file need not exist)
a - open for appending (file need not exist)
r+ - open for reading and writing, start at beginning
w+ - open for reading and writing (overwrite file)
a+ - open for reading and writing (append if file exists)
Following is the declaration for fseek() function.
int fseek(FILE *stream, long int offset, int whence)
SEEK_SET Beginning of file
SEEK_CUR Current position of the file pointer
SEEK_END End of file
Following fputc() example :
/* fputc example: alphabet writer */
#include <stdio.h>
int main ()
{
FILE * pFile;
char c;
pFile = fopen ("alphabet.txt","w");
if (pFile!=NULL) {
for (c = 'A' ; c <= 'Z' ; c++)
fputc ( c , pFile );
fclose (pFile);
}
return 0;
}

It depends on your current position/offset for an example if your file pointer was on 100th offset and you write fseek(f, -1, SEEK_CUR); and the offset will be at 99th position, and then you write space on 99th position, after writing space using fputc(' ', f); file pointer's offset will be 100th again.

Related

How to use fgets after using fgetc?

I'm trying to write a specific program that reads data from a file but I realized that when I read the file with fgetc, if I use fgets later, it doesn't have any output.
For example, this code:
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE * arq = fopen("arquivo.txt", "r");
char enter = fgetc(arq);
int line_count = 1;
while(enter != EOF) {
if (enter == '\n') line_count++;
enter = fgetc(arq);
}
printf("%d", line_count);
char str[128];
while(fgets(str, 128, arq)) printf("%s", str);
}
the second while doesn't print anything but if I delete the first while, the code prints the file content. Why is that happening?
TLDR: rewind(arq); is what you want
When you read from a file, the internal file pointer advances as you read, so that each subsequent read will return the next data in the file. When you get to the end, all subsequent reads will return EOF as there is nothing more to read.
You can manipulate the internal file pointer with the fseek and ftell functions. fseek allows you to set the internal file pointer to any point in the file, relative to the beginning, the end, or the current position. ftell will tell you the current position. This allows you to easily remember any position in the file and go back to it later.
SYNOPSIS
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
long ftell(FILE *stream);
void rewind(FILE *stream);
DESCRIPTION
The fseek() function sets the file position indicator for the stream pointed to by stream.
The new position, measured in bytes, is obtained by adding offset bytes to the position
specified by whence. If whence is set to SEEK_SET, SEEK_CUR, or SEEK_END, the offset is
relative to the start of the file, the current position indicator, or end-of-file, respec‐
tively. A successful call to the fseek() function clears the end-of-file indicator for
the stream and undoes any effects of the ungetc(3) function on the same stream.
The ftell() function obtains the current value of the file position indicator for the
stream pointed to by stream.
The rewind() function sets the file position indicator for the stream pointed to by stream
to the beginning of the file. It is equivalent to:
(void) fseek(stream, 0L, SEEK_SET)
except that the error indicator for the stream is also cleared (see clearerr(3)).
One caveat here is that the offsets used by fseek and returned by ftell are byte offsets, not character offsets. So when accessing a non-binary file (anything not opened with a "b" modifier to fopen) the offsets might not correspond to characters exactly. It should always be ok to pass an offset returned by ftell back to fseek unmodifed to get to the same spot in the file, but trying to compute offsets otherwise may be tricky.

Why is the file size different with the times of file read of 1 bytes?

I'm learning the file handling of C and I got some problem.
I wrote the codes as follows,
# include <stdio.h>
# include <stdlib.h>
int main(void)
{
FILE * file;
errno_t err = fopen_s(&file,"f.txt","r");
fseek(file, 0, SEEK_END);
int size = ftell(file);
fseek(file, 0, SEEK_SET);
char *tmp;
tmp = malloc(size);
printf("%d\n", size);
for (int i = 0; !feof(file); i++)
{
fread(tmp + i, 1, 1, file);
size = i + 1;
}
printf("%d\n", size);
fclose(file);
free(tmp);
return 0;
However, the outputs of size are not the same(1st: 78, 2nd: 76), what is the reason behind this?
I suspect you are using Microsoft Windows. In Microsoft’s C/C++ implementation, binary streams and text streams are different. If you had opened the file with "rb" passed to fopen_s as its third parameter, the file would be opened with a binary stream, and fread would return the actual bytes in the file.
Since you opened the file with "r", it was opened as a text stream. In this mode, some processing is performed when reading and writing the file. Notably, Windows uses two characters, a new-line '\n' and a carriage-return '\r', at the end of each line. When reading the file as a text stream, these two characters are reduced to a single '\n'. Conversely, when writing a text stream, writing a '\n' produces '\n' and '\r' in the file.
For a binary stream, ftell gives the number of bytes from the beginning of the file. For a text stream, the C standard only specifies that ftell is usable for resetting the stream position using fseek—it is not necessarily the number of bytes (in the actual file) or characters (appearing in the stream) from the beginning of the file. A C implementation might implement ftell so that it gives the number of bytes from the beginning of the file (and that is the 78 you are seeing), but, even if it does, you cannot easily use that to know how many characters are in the text stream.
Additionally, as others have noted in comments, this code is wrong:
for (int i = 0; !feof(file); i++)
{
fread(tmp + i, 1, 1, file);
size = i + 1;
}
The standard library routines do not know the end of the file file has been reached until you attempt a read and it fails because the end of the file was reached. For example, if there is one character in the file, and you read it, feof(file) is still false—the end of the file has not been encountered. It is not until you try to read a second character and fread fails that feof(file) becomes true.
Because of this, the above loop ultimately sets size to one more than the number of characters read because, at the beginning of the file iteration, !feof(file) was true, so fread was attempted, it failed, and then size was set to i + 1 even though no byte was just read.
Because this is how feof works, you could not use it for controlling a loop like this. Instead, you should write the loop so that it tests the result of fread and, if it fail to read any characters, the code exits the loop. That code could be something like:
int i = 0;
do
{
size_t result = fread(tmp + i, 1, 1, file);
if (result == 0)
break;
i++;
}
size = i;
(Note that, if you were reading more than one byte at a time with fread, additional code would be needed to handle the case where the number of bytes read was between zero and the number requested.)
Once that loop is fixed, you should see the number of characters in the stream reported as 75. Most likely, your file f.txt contains three lines of text with 72 characters total excluding the line endings. When read as a text stream, there are three '\n' characters, so the total is 75. When read as a binary stream, there are three '\n' characters and three '\r' characters, so the total is 78.

In C file reading: how do execute fgets on a same file multiple times?

For example;
I have a while loop that loop through the file
while (fgets(line, MAXLINE-1, filePointer)!=NULL){
// do something;
}
fclose(filePointer);
after the loop finish, if I call fgets on the same file will return me null because fgets already finish the whole file. Then how should I read through the file again if I want to use fgets()?
At the end of the while loop what happens is that the file pointer reaches to the end of the file(known as EOF). The OS keeps a record of the position of the pointer to keep track of where you are or what your position is in the file so that it can read further from that position. All you need to do is just move that file pointer to the beginning of the file. Now there are two ways for that you can use the rewind() method or the fseek() method. This is how:
rewind(fp);
OR
fseek(fp, 0, SEEK_SET);
use this before reading the same file again. for the record, fp refers to the file that you are trying to read
To reset the file pointer, you may use two approaches:
rewind(fp);
and:
fseek(fp, 0L, SEEK_SET);
Where fp is the FILE pointer. You may then call fgets() again to read the file from its beginning.

Read binary and txt File - 1 byte at a time

I am trying to write a code which reads 1 byte (ideal goal is n bytes but starting with 1 byte - so for n bytes if its easier please suggest)
Below is the code I have attempted to read 1 byte at a time and output it in hex format. But all get is bunch of FFFF
FILE *fp;
int stringlength,i;
/* File can be txt or .bin */
fp = fopen("TestFile3.txt", "r");
if (fp == NULL)
{
puts("Error: Input file cannot be read");
return -1;
}
else
{
size_t i, strlength, lengthOfFile,c;
fseek(fp, 0, SEEK_END);
lengthOfFile = ftell(fp);
printf("length of File is ---- %d \n", lengthOfFile);
while (lengthOfFile)
{
c = fgetc(fp);
printf("%c", c);
lengthOfFile--;
}
putchar('\n');
}
fclose(fp);
return 0;
}
You need fseek(fp, 0, SEEK_SET); to reset the file pointer before the while loop.
You're also opening the file in "text" mode:
fp = fopen("TestFile3.txt", "r");
Per the C Standard, section 7.19.2:
A text stream is an ordered sequence of characters composed into
lines, each line consisting of zero or more characters plus a
terminating new-line character. Whether the last line requires a
terminating new-line character is implementation-defined. Characters
may have to be added, altered, or deleted on input and output to
conform to differing conventions for representing text in the host
environment. Thus, there need not be a one- to-one correspondence
between the characters in a stream and those in the external
representation.
Using fseek()/ftell() doesn't return the number of bytes readable from a text stream.
You need to open the file in binary mode if you want to read every byte per the size of the file:
fp = fopen("TestFile3.txt", "rb");
Finally, the use of fseek()/ftell() isn't reliable on binary files either, because, again per the C standard, 7.19.9.2:
A binary stream need not meaningfully support fseek calls with a
whence value of SEEK_END
Given that, you can't reliably use fseek()/ftell() to find out how big a binary file is, either. And yes, examples do exist.
To reliably read all the bytes in a file, #Weather Vane posted one way to do that in the comments.

Why the ftell returns 0 in this function?

When I run my program and I choose to see the product list, it doesn't print anything. After some time, I find out that the value of fl_size is always 0. Why is this?
void view_prdct_code_list() {
FILE *stock = fopen("stock.dat","r+");
assert(stock);
int fl_size=ftell(stock);
int prd_size= sizeof(product);
int quantity= fl_size/prd_size;
printf("fl_size=%d",fl_size);
fseek(stock,0,SEEK_SET);
prdct cprd= (product *)malloc (sizeof(product)*quantity);
assert(cprd);
int i;
fread(cprd,prd_size,quantity,stock);
for (i=0;i<quantity;i++){
printf("PRODUCT CODE: %d\n",cprd->code);
}
free(cprd);
fclose(stock);
}
ftell does not return the total size of the file; it returns the current read or write position within the file. You call ftell immediately after opening the file, so that position is the very beginning of the file. You can either use fseek(stock, 0, SEEK_END) to seek to the end before calling ftell, or you can drop down a layer and use fstat(fileno(stock)) to retrieve the file size directly from the OS.
Further notes:
Neither of these options will work if you are reading from a pipe. (In general, you need to check for the success of every single one of your file access operations.)
fread is not guaranteed to read the entire file in one gulp even if you ask it to.
As 'alk' points out, ftell returns a long, not an int.
If long is only 32 bits, it is possible for a file to be so large that its size does not fit in a long. If your program needs to handle files that big, you need to #define _FILE_OFFSET_BITS 64 at the top of every .c file (before all the includes) and use fseeko and ftello. (Or whatever the Windows equivalent is.)
You should be opening this apparently-binary file with mode "r+b".
Binary files with no file header (and in particular with no magic number, of at least four bytes, at offset zero) are a Bad Thing.
Do not cast the return value of malloc. (It is necessary to do this in C++, but in C it is not only unnecessary, it can hide bugs.)
Check man page for ftell, for example this one: http://linux.die.net/man/3/ftell
Here is the relevant part: "The ftell() function obtains the current value of the file position indicator for the stream pointed to by stream."
When you open the file, cursor position will be at the start. So distance from start will be zero. Therefore ftell returns zero.
To find the size of file, see this link: How can I get a file's size in C?. Here's a snippet in short:
fseek(fp, 0L, SEEK_END);
sz = ftell(fp);
Make sure to call fseek(fp, 0L, SEEK_SET); after above.
Because ftell returns the size from the beginning to the current position of the file.
fseek(stock,0,SEEK_SET);
Means you set the position to the first bite of the file.
Also you gotta set fl_size=ftell(stock); after the fseek.

Resources