Going backwards by one in file stream - c

Say I have a file pointer like this:
file_ptr = fopen(“test.txt”, “r+”);
and I want to change each char's ASCII value as I move through the stream (I am doing this as a cipher, so if there is a better or more efficient way to do this, please let me know).
I am trying to use a while(!feof(file_ptr)) {} loop with fgetc() and fputc, but the file_ptr will be pointing to the next char, so i want to know if there is something i can do point it backwards one spot.
Essentially, I want to know the file stream equivalent for:
char* blah="blah blah";
char* index=blah;
index++;/*how to navigate a file stream in this way*/

Yes: you can use the fseek function:
fseek(file_ptr, -1L, SEEK_CUR); // move backwards one character
Furthermore, after your fputc and before your fgetc, you'll want to call fseek again:
fseek(file_ptr, 0L, SEEK_CUR); // don't move
just to notify the system that you're switching between reads and writes, so that it knows to properly flush all buffers and whatnot. (Otherwise you might not actually be where you think.)
By the way, for this to work properly, you'll want to open the file in binary mode rather than text mode:
file_ptr = fopen("test.txt", "rb+");
since otherwise the within-C definition of "character" might not exactly match the inside-the-file definition of "character" (especially when it comes to line endings), and fseek might put you in the middle of one of the latter.

Related

Read from file number of characters from a given position - C

I have a file called "cache.txt", where I have some data. I have a unordered_map, cache, with a pair of ints as value, where : the first element from the pair represents the number of characters I want to read, and the second the position from where I want to read. The key from the unordered_map, comps, is not relevant.
....
fp = fopen("cache.txt", "r");
fseek(fp, cache[comps].second, SEEK_SET);
int number_of_chars = cache[comps].first;
char c;
while((c = getc(fp)) != EOF && number_of_chars > 0) {
--number_of_chars;
printf("%c",c);
}
fclose(fp);
I have to use it several times, so that's the reason for opening and closing the file each time.
If you are using text streams (and apparently you are, because you open the file in mode "r", not "rb"), then the only position values you can pass to fseek are 0 or some value returned by ftell. In other words, you cannot count characters read yourself and use that count in a call to fseek.
If you use binary streams, then you can count the bytes read yourself, but you will have to deal with the fact that whatever the OS uses to represent newlines will not be translated to a newline character. Because there is no way to know how a given OS handles newlines, it's impossible to portably read a text stream in binary mode.
In short, you should take the requirement in the C standard library seriously. Make sure that the offset you are storing in your map came directly from a call to ftell. ("Directly" means that you cannot even use ftell(file) + 1; only ftell(file).)
Unix/Linux programmers can get away with not dealing with the above, since Posix mandates that there is no difference between text and binary modes, and only requires that fseek use a value returned by ftell for wide streams. But if you are using Windows, you will find that trying to use fseek to return to a byte position you computed yourself does not work. And if you are not using Windows, you should ask yourself whether your program might someday be ported to Windows.

fwrite append even the file position pointer is at the correct place

I want to overwrite an struct in a binary file. Here's my code
:
struct my_st {
char value[10];
};
void replace(char value[10]){
FILE *fpointer;
fpointer = fopen("data.dat", "rb+");
struct my_st x;
struct my_st new;
new.value="test";
while(1) {
fread(&x,sizeof(x),1,fpointer);
if(strcmp(x.value,value)==0)
break;
}
fwrite(&new,sizeof(x),1,fpointer);
}
I even check the place of file position pointer by printing it's value before fwrite and it was correct but it just append new data at the end of file and do not replace.
any suggestions?
If you open a file for update (+) and if you do one or more read operations, you must do a positioning operation (e.g. fseek()) before you do any writes. If you do one or more write operations, you must do a positioning operation (e.g. rewind()) before you do any reads. See POSIX's specification of fopen() for example.
When a file is opened with update mode ('+' as the second or third character in the mode argument), both input and output may be performed on the associated stream. However, the application shall ensure that output is not directly followed by input without an intervening call to fflush() or to a file positioning function (fseek(), fsetpos(), or rewind()), and input is not directly followed by output without an intervening call to a file positioning function, unless the input operation encounters end-of-file.
You are doing no positioning operations between reads and writes. That, in and of itself, leads to undefined behaviour.
Assuming your implementation exhibits 'undefined behaviour' by not going out of its way to misbehave, after your last fread(), you will write over the next entry — or append a new entry if the last one read was at the end of the file.
Decide where you want the data written.
Seek to the correct location (use fseek(fp, 0L, SEEK_CUR) if you don't want to move the read/write pointer).
Write.
If you'll be reading next, do another seek — another no-op if need so be.

How does fgets() keep track of what line it's on?

This code correctly reads a file line-by-line, stores each line in line[] and prints it.
int beargit_add(const char* filename) {
FILE* findex = fopen(".beargit/.index", "r");
char line[FILENAME_SIZE];
while(fgets(line, sizeof(line), findex)) {
strtok(line, "\n");
fprintf(stdout, "%s\n", line);
}
fclose(findex);
return 0;
}
However, I am baffled as to why using fgets() in the while loop actually reads the file line-by-line. I am brand new to C after having learned Python and Java.
Since each call to fgets() is independent, where is C remembering which line it is currently on each time you call it? I thought it might have to do with changing the value FILE* index points to, but you are passing the pointer into fgets() by value so it could not be modified.
Any help in understanding the magic of C is greatly appreciated!
It's not fgets keep track, findex does this for you
findex is a FILE type, which includes infomation about a open file such as file descriptor, file offset
FILE is a encapsulation of I/O in OS.
more about: FILE
Object type that identifies a stream and contains the information needed to control it, including a pointer to its buffer, its position indicator and all its state indicators.
and the offset is to keep track of the file, each time you read the file, it starts from the offset. All these work are done by FILE, it do for you, and for fgets
more info about offset offset wiki
I thought it might have to do with changing the value FILE* index points to
it's not the value of the pointer itself that is changed. The pointer points to a structure (of type FILE) which contains information about the stream associated with that structure/pointer. Presumably, one of the members of that structure contains a "cursor" that points to the next byte to be read.
Or maybe it's just got a file descriptor int (like on many Unices) and I/O functions just call out to the kernel in order to obtain information about the file descriptor, including the current position.

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.

fread() behaves weird

I have a problem in a C program of mine where after I use fread(), the file pointer goes to the end of the file sometimes.
I'll try to explain better - the code looks something like:
dummy = ftell(fp);
fread(&buf, sizeof(unsigned char), 8, fp);
dummy = ftell(fp);
where fp is a file pointer to an opened file (opened it with "w+", I'm using it as a binary file and I know i'm supposed to have a "b" in there too, but I heard its not really important to add it..),
dummy is just an unsigned long variable,
and buf is unsigned char[8]
now, when debugging, at the ftell before the fread, dummy is 262062
at the ftell after the fread, dummy is 262640
even though I only 'moved' 8 bytes..
does anyone have any idea what can be the cause of this..?
thanks for your help :)
If you don't use the b to open the file, ftell() doesn't return the truth, just a sort of "cookie" that's only useful to fseek(). There are a lot of different implementations out there; check the man page for your system to find out more.

Resources