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.
Related
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.
I know there are several asks already regarding this topic, but it seemed like those were either not completely answered or hard to apply to my code, so I apologize if this is a repeat.
I am having trouble with the below function in an overall I/O program that also does word and line count (those work). char* filename is pulled from the command line. In this example it is pulling from a txt file with lorum ipsum. (69 words) In theory the below function should read from filename and write it to an array. Then read that array and checks if the current character is a space ' ' and the next character is not. It currently returns 0 regardless.
int wordcount(char* filename) {
int wc=0,i=0,z=0;
char w, test[1000];
FILE *fp;
fp = fopen(filename, "r");
while (feof(fp) == 0) {
fscanf(fp, "%c", &test[i]);
i++;
}
while (z>i-1) {
if (test[z] = ' ' && test[z+1] != ' ' ) {
wc++;z++;
}
}
return wc;
}
NOTES: i know it's super inefficient to declare a 1000 char array, but I wasn't sure how else to do it. If you have any improvements or other methods to accomplish this, it would be greatly appreciated if you shared. Also, i'm aware that this currently ignores others types of whitespace, but I am just testing this first and will expand after.
Thanks for any assistance.
There is a sample function doing what you need. Some suggestions for you code, fopen() must be followed by fclose() when you no longer need the file. Always check if the pointer returned by fopen is not NULL and do nothing in that case, just return error code. The presence of new word can be safely detected by the space character followed by a non space character in that case increment world count ++wc. Use getc() to read one character from the file object and use isspace() function to check if the character is a space one. You don't need an array to store the file if no one modifies that file during the worldcount run.
int wordcount(const char* filename)
{
int wc=0;
char c;
FILE *fp;
fp = fopen(filename, "r");
if(fp == NULL)
{
return -1;
}
bool previsspace = 1;
while ((c=getc(fp)) != EOF)
{
if (isspace(c)==0 && (previsspace==1)) ++wc;
previsspace = isspace(c);
}
fclose(fp);
return wc;
};
You will need the following include files:
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
The concrete problem why you always get a result of 0 words:
while (z>i-1) {
z is never larger than i-1. Probably you meant to loop while z is smaller than i-1 instead:
while (z<i-1) {
Additionally you only increment z when you find a word. You should increment it for every character you test, no matter if it's a space or not.
hello i wrote below a program in c,
#include <stdio.h>
#include <conio.h>
void main()
{
FILE *fp;
char c;
long n=0L;
fp=fopen("myfile.txt","w");
printf("Enter the data into file:\n");
while((c=getchar())!=EOF)
{
putc(c,fp);
}
printf("Total character into file:%ld\n",ftell(fp));
fclose(fp);
fp=fopen("myfile.txt","r");
while(feof(fp)==0)
{
fseek(fp,n,0);
printf("\n char:'%c' at position '%ld'",getc(fp),ftell(fp));
n++;
}
fclose(fp);
getch();
}
it work fine but when i replace the statement:
printf("\n char:'%c' at position '%ld'",getc(fp),ftell(fp));
with the statement:
printf("\n position '%ld'",ftell(fp));
then it will going to infinite loop
I knew the function, fseek()It set the file pointer to specified position.
but here what happen,i don't understand.
please help me.
Reason for infinite loop : In your second case you do not progress the stream as in first case
getc()
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.
A solution :
#include <stdio.h>
void main()
{
FILE *fp;
char c;
long n = 0L;
fp=fopen("main.c","r");
while(getc(fp)!=EOF)
{
fseek(fp,n,0);
printf("\n position '%ld'",ftell(fp));
n++ ;
}
fclose(fp);
}
Furthermore :
fseek() do not progress the file pointer.
Sets the position indicator associated with the stream to a new position.
feof()
This indicator is generally set by a previous operation on the stream
that attempted to read at or past the end-of-file."
So this explains your failure. Second implementation does not come across EOF as first case. Provided sample of code would provide you an workaround.
Check in documentation if fseek or ftell sets feof() result. Possibly not, so when you removed getc call feof can no longer return 'true', hence infinite loop.
Anyway, are you interested in the explanation why this program behaves strange or rather how you should write your program properly to achieve the result described...?
fseek() will not set the EOF marker, and in fact it can unset the EOF marker and it should do it after a succesful call.
Also, as the link points out the while (feof(fp) == 0) is always wrong, it's because you need to fgetc() one extra char for the EOF marker to be set, so you will have one extra iteration, instead you have to do it this way
#include <stdio.h>
int main()
{
FILE *fp;
int c; /* this should be 'int' and not 'char' */
long int n;
fp = fopen("myfile.txt", "w");
if (fp == NULL)
return -1;
printf("Enter the data into file:\n");
while ((c = getchar()) != EOF)
putc(c, fp);
printf("Total character into file:%ld\n", ftell(fp));
fclose(fp);
fp = fopen("myfile.txt","r");
if (fp == NULL)
return -1;
while ((c = fgetc(fp)) != EOF)
{
if (c == '\n')
printf("charcater `\\n' at position '%ld'\n", ftell(fp));
else
printf("charcater `%c' at position '%ld'\n", c, ftell(fp));
n++;
}
fclose(fp);
return 0;
}
A successful call fseek function clear the end-of-file indicator, but your loop condition rely on feof, so feof(fp) == 0 always true.
fseek(SEEK_SET) merely sets the FILE's pointer to a position in the file. It doesn't actually do any reading of the file or anything. So, it will gladly let you set the position to WAY past the end of the file (and it clears any existing EOF set on the stream). ftell() merely returns the position of the file pointer.
Without doing a read of some sort (e.g. - getc()), EOF won't be set on the stream and your loop will be infinite.
You can do a fseek(fp, 0, SEEK_END) and then an ftell() to know the size / end of the file position.
PS - You really need to check your return values more carefully and handle any failures.
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.
The Problem was to find and replace a string in a C File.
I am new to C Files. I have tried the following code but I didnt get any output:
#include<stdio.h>
#include<string.h>
int main()
{
FILE *f1,*f2;
char *src,*dest,*s1,ch,ch1,ch2,ch3;
int i;
f1=fopen("input.txt","rw");
f2=fopen("dummy.txt","rw");
src="mor";
dest="even";
while(ch!=EOF)
{
ch=fgetc(f1);
if(ch==src[0]) //Finding 1st char of src
{
fgets(s1,strlen(src),f1);
if(strcmp(src+1,s1)==0) //Finding occurance of "src" in file
{
fseek(f1,strlen(src)-1,SEEK_CUR);
while(ch1!=EOF) //Copying remaining data into another file
{
ch1=fgetc(f1);
fputc(ch1,f2);
}
fseek(f1,-strlen(src),SEEK_CUR);
for(i=0;i<strlen(dest);i++) //replacing "src" with "dest"
{
ch2=dest[i];
fputc(ch2,f1);
}
fclose(f1);
f1=fopen("input.txt","a");
while(ch3!=EOF) //Appending previosly copied data into 1st file
{
ch3=fgetc(f2);
fputc(ch3,f1);
}
}
}
}
fclose(f1);
fclose(f2);
}
The Contents of input.txt is "morning".
Kindly point the ERROR in the logic and also give an efficient code for the same.
Thanks in Advance.
Reading files in C is usually a bit messy. The first problem that I see is the way ch is used in the main loop. The first time
while (ch != EOF)
is executed, ch is uninitialized, and if it happens to hold EOF, the main loop will not execute at all. I usually use the following structure for reading from files:
FILE *fInput = fopen("input.txt", "r");
int ch; /* need an int to hold EOF */
for (;;)
{
ch = fgetc(fInput);
if (ch == EOF) break;
...
}
In addition, you may need to read up on file pointer concept. For example, after reading the remainder of src, you fseek() forward, and skip some more characters before you copy data to f2. Essentially, you read m, read or (with fgets() - and into an unallocated buffer s1 that would go ka-boom on you some time in the near future), skip 2 more characters (now your pointer is at last n of "morning"), copy "ng" into f2, try to write EOF to f2 in this loop (hence the above pattern for reading until EOF), seek two characters back (which may fail once you reach EOF, my C file functions are a bit rusty these days), write "even" to f1 (which should, if I am wrong about seek after EOF, set input file to "mornieven", and not change it if I am correct). In summary, I don't think the code does what you intend it to do.
I would recommend building up your function. Each one of the following can be written as a program that you should test and finish before going to next step:
read the file safely, and print it out
detect the contents of src, and print the rest of input
save the rest of the input to second file instead of printing
replace src with dest in first file, and ignore the rest (since you open input file with 'rw', this will truncate the rest of input). You may need to do an fseek() to clear the EOF status. Also look at ftell() to record a position that you can jump back to using fseek()
finally, copy in everything you have saved to second file after replacing src with dest (no need to close f1 here. But it is better to open f2 as write, close after copy from first file, and reopen as read to perform the copy back to f1).
In addition, when you need a buffer (such as s1), just use a large enough array for now, but look into malloc() and free() functions to perform dynamic memory allocations for situations like these.
One simple way to do the replace would be to first read in the whole file into a buffer
e.g.
FILE* fpIn = fopen("file.txt","rb");
fseek(fpIn, 0L, SEEK_END);
size_t s = ftell(fpIn);
fseek(fpIn, 0L, SEEK_SET);
void* buf = malloc(s);
fread(buf,s,1,fpIn);
now while writing the file, check for your string
char src[] = "mor";
char dest[] = "even";
int lenSrc = strlen(src);
int lenDest = strlen(dest);
for (char* ch = buf; ch < buf + s; ++ch)
{
if ( !memcmp( ch, src, lenSrc ) )
{
fwrite( dest, 1,lenDest, fpOut );
ch += lenSrc;
}
else
{
fputc( *ch, fp );
}
}
disclaimer: haven't compiled this
You are printing the wrong thing in your output. Print, "ch", not the file pointer.
while(ch!=EOF)
{
ch=getc(f1);
printf("%c",ch);
}
while(ch!=EOF)
{
ch=getc(f2);
printf("%c",ch);
}
Also, f2 is closed at the end during your output. You'll have to reopen it (just like you do with f1.)
At first glance, I see that your code to call fgets is wrong. You have not allocated any memory and you are reading a string into an uninitialized pointer. Read into an array or dynamically allocated memory.
Another problem is that you are declaring ch as char. fgetc() returns an int, and for good reason. It is good to be able to return any possible character or EOF, so EOF shouldn't be a character, so ideally fgetc() returns a bigger type than char.
The upshot is that the loop may well never end, since ch can't possibly hold EOF on some standard implementation. Declare it (and ch1 and ch3) as int.