I just want to replace specific character from file.
for example, I want to replace character 'l' with character 'p'.
is it correct way ?
int main() {
FILE * ptr;
ptr = fopen("D:\f4.txt", "r+");
if (ptr == NULL) {
printf("file cant be opened");
exit(0);
}
char ch = fgetc(ptr);
while (ch != EOF) {
if (ch == 'l') {
fseek(ptr, -1, 1);
fputc('p', ptr);
}
ch = fgetc(ptr);
}
fclose(ptr);
}
suppose content in my file is "hello everyone" so output should be like "heppo everyone" but it writes in file "hepepepepepepepepepepepepepepep" continuesly. please help me to find why this happen.
Please note this from the man page for fopen().
When the "r+", "w+", or "a+" access type is specified, both reading and writing are enabled (the file is said to be open for "update"). However, when you switch from reading to writing, the input operation must encounter an EOF marker. If there is no EOF, you must use an intervening call to a file positioning function. The file positioning functions are fsetpos, fseek, and rewind. When you switch from writing to reading, you must use an intervening call to either fflush or to a file positioning function. (my italics)
So after you wrote 'p' to the file, it is not enough to carry on reading as though nothing has happened, you must fseek to the original position, obtained by ftell, or fflush the file.
Also don't use magic numbers: in fseek you should use SEEK_CUR not 1.
Finally, function fgetc returns an int type, not char. This allows EOF to be distinguished from the data byte 0xFF.
Related
I want to modify some vowels of a file by "5". The following code works. However, I do not understand why I should put fseek twice.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
void print_file_contents(const char *filename)
{
FILE *fp;
char letter;
if((fp=fopen(filename,"r+"))==NULL)
{
printf("error\n");
exit(1);
}
fseek(fp,0,SEEK_END);
int size=ftell(fp);
rewind(fp);
for(int i=0;i<size;i++)
{
fseek(fp,i,SEEK_SET);
letter=fgetc(fp);
if((letter=='a') || (letter=='e') || (letter=='i'))
{
fseek(fp,i,SEEK_SET); // WHY THIS FSEEK ?
fwrite("5",1,sizeof(char),fp);
}
}
fclose(fp);
}
int main(int argc, char *argv[])
{
print_file_contents("myfile");
return 0;
}
In my opinion, the first fseek(fp, i, SEEK_SET) is used to set the file position indicator to the current character being processed, so that the character can be read using fgetc. Hence, the cursor is updated every time so there is no need to add another fseek(fp, i, SEEK_SET);.
The fgetc advanced the file position; if you want to replace the character you just read, you need to rewind back to the same position you were in when you read the character to replace.
Note that the C standard mandates a seek-like operation when you switch between reading and writing (and between writing and reading).
§7.21.5.s The fopen function ¶7:
¶7 When a file is opened with update mode ('+' as the second or third character in the above list of mode argument values), both input and output may be performed on the associated stream. However, output shall not be directly followed by input without an intervening call to the fflush function or to a file positioning function (fseek, fsetpos, or rewind), and input shall not be directly followed by output without an intervening call to a file positioning function, unless the input operation encounters end- of-file.
Also, calling fgetc() moves the file position forward one character; if the write worked (it's undefined behaviour if you omit the seek-like operation), you'd overwrite the next character, not the one you just read.
Your intuition is correct: two of the three fseek calls in this program are unnecessary.
The necessary fseek is the one inside the if((letter=='a') || (letter=='e') || (letter=='i')) conditional. That one is needed to back up the file position so you overwrite the character you just read (i.e. the vowel), not the character after the vowel.
The fseek inside the loop (but outside the if) is unnecessary because both fgetc and fwrite advance the file position, so it will always set the file position to the position it already has. And the fseek before the loop is unnecessary because you do not need to know how big the file is to implement this algorithm.
This code can be tightened up considerably. I'd write it like this:
#include <stdio.h>
void replace_aie_with_5_in_place(const char *filename)
{
FILE *fp = fopen(filename, "r+"); // (1)
if (!fp) {
perror(filename); // (2)
exit(1);
}
int letter;
while ((letter = fgetc(fp)) != EOF) { // (3)
if (letter == 'a' || letter == 'e' || letter == 'i') { // (4)
fseek(fp, -1, SEEK_CUR); // (5)
fputc('5', fp);
if (fflush(fp)) { // (6)
perror(filename);
exit(1);
}
}
if (fclose(fp)) { // (7)
perror(filename);
exit(1);
}
}
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "usage: %s filename\n", argv[0]);
return 1;
}
replace_aei_with_5_in_place(argv[1]); // (8)
return 0;
}
Notes:
It is often (but not always) better to write operations with side effects, like fopen, separately from conditionals checking whether they succeeded.
When a system-level operation fails, always print both the name of any file involved, and the decoded value of errno. perror(filename) is a convenient way to do this.
You don't need to know the size of the file you're crunching because you can use a loop like this, instead. Also, this is an example of an exception to (1).
Why not 'o' and 'u' also?
Here's the necessary call to fseek, and the other reason you don't need to know the size of the file: you can use SEEK_CUR to back up by one character.
This fflush is necessary because we're switching from writing to reading, as stated in Jonathan Leffler's answer. Inconveniently, it also consumes the notification for some (but not all) I/O errors, so you have to check whether it failed.
Because you are writing to the file, you must also check for delayed I/O errors, reported only on fclose. (This is a design error in the operating system, but one that we are permanently stuck with.)
Best practice is to pass the name of the file to munge on the command line, not to hardcode it into the program.
#Jonathan Leffler well states why code used multiple fseek(): To cope with changing between reading and writing.
int size=ftell(fp); is weak as the range of returned values from ftell() is long.
Seeking in a text file (as OP has) also risks undefined behavior (UB).
For a text stream, either offset shall be zero, or offset shall be a value returned by an earlier successful call to the ftell function on a stream associated with the same file and whence shall be SEEK_SET. C17dr § 7.21.9.1 3.
Better to use #zwol like approach with a small change.
Do not assume a smooth linear mapping. Instead, note the location and then return to it as needed.
int replacement = '5';
for (;;) {
long position = ftell(fp);
if (ftell == -1) {
perror(filename);
exit(1);
}
int letter = fgetc(fp);
if (letter == EOF) {
break;
}
if (letter == 'a' || letter == 'e' || letter == 'i') {
fseek(fp, position, SEEK_SET);
fputc(replacement, fp);
if (fflush(fp)) {
perror(filename);
exit(1);
}
}
}
Research fgetpos(), fsetpos() for an even better solution that handles all file sizes, even ones longer than LONG_MAX.
I may be missing something here, but is it possible to change stdin to be a file pointer and then switch it back to the console?
Example:
stdin = fp;
for (int x; x < 10; x++)
{
c = getchar()
}
stdin = ??? // Return the stream to the console
The "official" answer is freopen(). Theoretically you can call
freopen("somefile", "r", stdin);
and now stdin is reading from "somefile". However, once you've done this it's either tricky or impossible to get stdin pointing back at standard input (or, as you called it, "the console") when you're done. See also questions 12.33 and 12.34 in the old C FAQ list.
But really: why are you trying to reassign stdin in this way? stdin is basically a global variable, and any time you have the pattern
change global variable;
make function call that implicitly uses global variable;
set global variable back to what it was;
you have poor design and a recipe for disaster. Usually what you want to do is to create a modified version of that function call in the middle -- whatever it is -- that lets you pass the something in as an explicit parameter, rather than implicitly using the global variable.
In this case, you don't even need to invent anything new, because instead of getchar() which implicitly reads from the global stdin, you can just call getc(fp), which reads from whatever file pointer you want to specify:
for (int x; x < 10; x++)
{
c = getc(fp);
}
Save it in another variable first.
FILE *save_stdin = stdin;
stdin = fp;
...
stdin = save_stdin;
Of course, there may be no need to change stdin in the first case. You could just use getc(fp) instead of getchar(). Reassigning stdin would only be necessary if you're calling code that uses stdin and can't be changed.
According to the man stdin:
Since the symbols stdin, stdout, and stderr are specified to be
macros, assigning to them is nonportable. The standard streams can be
made to refer to different files with help of the library function
freopen, specially introduced to make it possible to reassign stdin,
stdout, and stderr.
So, it is not safe to do that.
But, you can use fdopen with input stream file descriptor and reading mode to recover stdin as following:
stdin = fdopen(0, "r");
In the following code, the first loop reads the data from the file example, and the second loop reads characters from the standard input:
FILE *fp = fopen("example.txt", "r");
char c;
stdin = fp;
while ((c = fgetc(stdin)) != EOF) {
printf("%c", c);
}
stdin = fdopen(0, "r");
while ((c = fgetc(stdin)) != EOF) {
printf("%c", c);
}
fclose(fp);
return 0;
I have this code and I don't understand how it works:
void print(char * fileName)
{
FILE * fp;
int ch;
fp = fopen(fileName, "r");
while (ftell(fp) < 20)
{
ch = fgetc(fp);
putchar(ch);
}
fclose(fp);
}
So how is ftell(fp) works if it is in loop?
Because there is nothing inside the loop that get it up.
how it is progressive?
ftell() gets you the current value of the position indicator of the stream(in your case, it basically returns the character position it is currently pointing to right now).
fgetc() gets the next character (an unsigned char) from the specified stream and advances the position indicator for the stream. This function returns the character read as an unsigned char cast to an int or EOF on end of file or error
Flow of your program
What that means in very simple terms is -
fgetc() is reading one character after character from the file and advancing the pointer to the next character.
ftell() is returning you the current position in in bytes from the
beginning of the file. This means it tells the position of the character it is pointing right now(since 1 char takes 1 byte).
So, your program reads from the file until ftell() returns the
position which is less than 20.This means that it will keep looping until 20 characters have been read from your file.
Hope this clears your doubt !
ftell returns the current value of the file position indicator, and fgetc does advance the file position indicator within the loop.
But this program is wrong. For a stream opened in text mode ("r"), the return value of ftell cannot be used portably for anything else except for seeking to a previous position. From C11 draft n1570 7.21.9.4p2
[...] For a text stream, its file position indicator contains unspecified information, usable by the fseek function for returning the file position indicator for the stream to its position at the time of the ftell call; the difference between two such return values is not necessarily a meaningful measure of the number of characters written or read.
Indeed it doesn't make any sense to use ftell in this program. Either open the file in binary mode, "rb", and then it is guaranteed that
[...] the value is the number of characters from the beginning of the file.
or for counting characters read from text file, use a counter variable:
int c_read = 0;
while (c_read < 20)
{
ch = fgetc(fp);
putchar(ch);
c_read ++;
}
Finally neither your original version or mine does not work correctly if the file has less than 20 characters. In that case EOF is returned from fgetc and putchar would write (unsigned char)EOF to the stream (most likely a byte of value 255!)
Thus the correct code would be
int c_read = 0;
while (c_read < 20)
{
ch = fgetc(fp);
if (ch == EOF) {
// report the error
perror("Failed to read 20 characters");
break;
}
putchar(ch);
c_read ++;
}
I'm reading a book about c programming and don't understand a shown example. Or more precisely I don't understand why it works because I would think it shouldn't.
The code is simple, it reads the content of a text file and outputs it in output area. As far as I understand it, I would think that the
ch = fgetc(stream);
ought to be inside the while loop, because it only reads one int a time? and needs to read the next int after the current one has been outputted. Well, it turns out that this code indeed works fine so I hope someone could explain my fallacy to me. Thanks!
#include <stdio.h>
int main(int argc, char *argv[]) {
FILE *stream;
char filename[67];
int ch;
printf("Please enter the filename?\n");
gets(filename);
if((stream = fopen(filename, "r")) == NULL) {
printf("Error opening the file\n");
exit(1);
}
ch = fgetc(stream);
while (!feof(stream)) {
putchar(ch);
ch = fgetc(stream);
}
fclose(stream);
}
I think you are confuse because of feof():
Doc: int feof ( FILE * stream );
Checks whether the end-of-File indicator associated with stream is
set, returning a value different from zero if it is.
This indicator is generally set by a previous operation on the stream
that attempted to read at or past the end-of-file.
ch = fgetc(stream); <---"Read current symbol from file"
while (!feof(stream)) { <---"Check EOF read/returned by last fgetc() call"
putchar(ch); <---"Output lasts read symbol, that was not EOF"
ch = fgetc(stream); <---"Read next symbols from file"
}
<-- control reach here when EOF found
A much better way is to write your loop like:
while((ch = fgetc(stream))!= EOF){ <--" Read while EOF not found"
putchar(ch); <-- "inside loop print a symbol that is not EOF"
}
Additionally, Note: int fgetc ( FILE * stream );
Returns the character currently pointed by the internal file position
indicator of the specified stream. The internal file position
indicator is then advanced to the next character.
If the stream is at the end-of-file when called, the function returns
EOF and sets the end-of-file indicator for the stream (feof).
If a read error occurs, the function returns EOF and sets the error
indicator for the stream (ferror).
If the fgetc outside while is removed, like this:
while (!feof(stream)) {
putchar(ch);
ch = fgetc(stream);
}
ch will be un-initialized the first time putchar(ch) is called.
By the way, don't use gets, because it may cause buffer overflow. Use fgets or gets_s instead. gets is removed in C11.
The code you have provided has 'ch =fgetc(stream);' before the While loop and also
'ch = fgetc(stream);' within the body of the loop.
It would be logical that the statement within the loop is retrieving the ch from the stream one at a time as you correctly state.
It is inside and outside as you see. The one outside is responsible for reading the first character (which may be already the end of file, then the while wouldn't be entered anyway and nothing is printed), then it enters the loop, puts the character and reads the next one.. as long as the read character is not the end of file, the loop continues.
This is because of second fgetc which is getting call upto while (!feof(stream)).
fgetc() reads a char(byte) and return that byte,The reading of byte value depends on where the read pointer is available.
Once fgetc() successfully read one byte the read file pointer moves to the next byte .so if you read the file the next byte will be the output and it will continue upto it find the end of the file where it return EOF.
Actually this part here:
while (!feof(stream)) {
putchar(ch);
ch = fgetc(stream);
}
is pretty unsafe and you should avoid checking EOF like that (here why).
The way you should read a file using fgetc is like so:
int ch;
while ((ch = fgetc(stream)) != EOF)
{
printf("%c", ch)
}
This is non functional code. Last character from file is never outputted. fgetc will read last character and pointer will be at end of file. So, when while is checked, !feof will return false, and read character will not be outputed.
feofis not preventing reading after end of file: for empty files fgetc will be called before feof!
Unless there is some benefit in console handling, two better options exist:
Using feof:
while (!feof(stream)) {
ch=fgetc(stream);
putchar(ch);
}
Without using feof - because fgetc returns EOF when there are no more characters:
while ((ch=fgetc(stream))!=EOF) putchar(ch);
I'm trying to read a file and replace every char by it's corresponding char up one in ASCII table. It opens the file properly but keep on reading the first character.
int main(int argc, char * argv[])
{
FILE *input;
input = fopen(argv[2], "r+");
if (!input)
{
fprintf(stderr, "Unable to open file %s", argv[2]);
return -1;
}
char ch;
fpos_t * pos;
while( (ch = fgetc(input)) != EOF)
{
printf("%c\n",ch);
fgetpos (input, pos);
fsetpos(input, pos-1);
fputc(ch+1, input);
}
fclose(input);
return 1;
}
the text file is
abc
def
ghi
I'm pretty sure it's due to the fgetpos and fsetpos but if I remove it then it will add the character at the end of the file and the next fgetc will returns EOF and exit.
You have to be careful when dealing with files opened in update mode.
C11 (n1570), § 7.21.5.3 The fopen function
When a file is opened with update mode ('+' as the second or third character in the
above list of mode argument values), both input and output may be performed on the
associated stream.
However, output shall not be directly followed by input without an
intervening call to the fflush function or to a file positioning function (fseek,
fsetpos, or rewind), and input shall not be directly followed by output without an
intervening call to a file positioning function, unless the input operation encounters end-of-file.
So your reading might look something like :
int c;
while ((c = getc(input)) != EOF)
{
fsetpos(/* ... */);
putc(c + 1, input);
fflush(input);
}
By the way, you will have problems with 'z' character.
procedure for performing random access such
positioned the record
reading of the record
positioned the record
update(write) the record
do flush (to finalize the update)
The following code is a rewrite in consideration to it.
#include <stdio.h>
#include <ctype.h>
int main(int argc, char * argv[]){
FILE *input;
input = fopen(argv[1], "rb+");
if (!input){
fprintf(stderr, "Unable to open file %s", argv[1]);
return -1;
}
int ch;
fpos_t pos, pos_end;
fgetpos(input, &pos);
fseek(input, 0L, SEEK_END);
fgetpos(input, &pos_end);
rewind(input);
while(pos != pos_end){
ch=fgetc(input);
if(EOF==ch)break;
printf("%c",ch);
if(!iscntrl(ch) && !iscntrl(ch+1)){
fsetpos(input, &pos);
fputc(ch+1, input);
fflush(input);
}
pos += 1;
fsetpos(input, &pos);
}
fclose(input);
return 1;
}
I really suspect the problem is here:
fpos_t * pos;
You are declaring a pointer to a fpos_t which is fine but then, where are the infomation stored when you'll retrieve the pos?
It should be:
fpos_t pos; // No pointer
...
fgetpos (input, &pos);
fsetpos(input, &pos); // You can only come back where you were!
Reading the (draft) standard, the only requirement for fpos_t is to be able to represent a position and a state for a FILE, it doesn't seem that there is a way to move the position around.
Note that the expression pos+1 move the pointer, does not affect the value it points to!
What you probably want is the old, dear ftell() and fseek() that will allow you to move around. Just remember to open the file with "rb+" and to flush() after your fputc().
When you'll have solved this basic problem you will note there is another problem with your approach: handling newlines! You most probably should restrict the range of characters you will apply your "increment" and stipulate that a follows z and A follows Z.
That said, is it a requirement to do it in-place?
7.21.9.1p2
The fgetpos function stores the current values of the parse state (if
any) and file position indicator for the stream pointed to by stream
in the object pointed to by pos. The values stored contain unspecified
information usable by the fsetpos function for repositioning the
stream to its position at the time of the call to the fgetpos
function.
The words unspecified information don't seem to inspire confidence in that subtraction. Have you considered calling fgetpos prior to reading the character, so that you don't have to do a non-portable subtraction? Additionally, your call to fgetpos should probably pass a pointer to an existing fpos_t (eg. using the &address-of operator). Your code currently passes a pointer to gibberish.
fgetc returns an int, so that it can represent every possible unsigned char value distinct from negative EOF values.
Suppose your char defaults to an unsigned type. (ch = fgetc(input)) converts the (possibly negative, corresponding to errors) return value straight to your unsigned char type. Can (unsigned char) EOF ever compare equal to EOF? When does your loop end?
Suppose your char defaults, instead, to a signed type. (c = fgetc(input)) is likely to turn the higher range of any returned unsigned char values into negative numbers (though, technically, this statement invokes undefined behaviour). Wouldn't your loop end prematurely (eg. before EOF), in some cases?
The answer to both of these questions indicates that you're handing the return value of fgetc incorrectly. Store it in an int!
Perhaps your loop should look something like:
for (;;) {
fpos_t p;
/* TODO: Handle fgetpos failure */
assert(fgetpos(input, &p) == 0);
int c = fgetc(input);
/* TODO: Handle fgetc failure */
assert(c >= 0);
/* TODO: Handle fsetpos failure */
assert(fsetpos(input, &p) == 0);
/* TODO: Handle fputc failure */
assert(fputc(c + 1, input) != EOF);
/* TODO: Handle fflush failure (Thank Kirilenko for this one) */
assert(fflush(input) == 0);
}
Make sure you check return values...
The update mode('+') can be a little bit tricky to handle. Maybe You could just change approach and load the whole file into char array, iterate over it and then eventually write the whole thing to an emptied input file? No stream issues.