I am new to C programming and just writing a simple program to read all the lines from a text file and replace each number with a new one. Here is my code. It prints to the console for each line but not to the file. Can someone please suggest what is wrong with my code?
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE * file_ptr;
int num;
char line[128];
file_ptr = fopen (argv[1], "a+");
if(file_ptr==NULL)
{
printf("Error opening file");
}
if(file_ptr!=NULL)
{
while(fgets(line,128,file_ptr)!=NULL)
{
fputs("df",file_ptr);
printf("2");
}
}
fclose(file_ptr);
return(0);
}
The problem is that you're reading and writing from the same file, and your reads and writes interact.
Opening the file with the mode a+ (append, allowing reading) sets the file position at the beginning of the file, so the first call to fgets reads the first line. But in append mode, all writes are performed at the end of the file. So the first call to fputs sets the file position to the end of the file, then writes df. Since there's a single file position for both reading and writing, the next call to fgets is performed at the end of the file, and reads nothing.
The behavior of file position makes the mode a+ appropriate mostly when you want to read the whole current content of a file and then add something at the end.
Note that the only way to modify the content in the middle of a file is to replace a sequence of bytes by a sequence of bytes with the same length. So you can replace 12 by df, but you can't replace 123 by df: if you set the file position where 123 is located and write df, you'll end up with df3. To replace numbers by strings of potentially different length, you'll need to rewrite the file as a whole
When you want to completely modify a file, there are three main techniques:
Load the current content in memory, truncate the file to 0, write the new content.
Open the current file for reading, create a new file, write the new content to the new file, close the old file, then move (rename) the new file to overwrite the old file.
Rename the old file, create a new file with the original name, read the current content from the renamed file, write the new content and close the files.
The first method has a major downside: if your program crashes or the computer loses power, the file will be lost. Thus you should almost always use one of the other two approaches: they use more disk space but the added safety is almost always worth it.
The following code, which incorporated several oversights in the OP code
will tell the user about any error conditions that occur
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE * file_ptr;
char line[128];
if( 2 > argc )
{ // then invalid number of parameters
printf( "\nUsage: %s filename\n", argv[0]);
exit(EXIT_FAILURE );
} // end if
// implied else, file name parameter exists
if( NULL == (file_ptr = fopen (argv[1], "a+") ) ) // open for read and append
{ // then fopen failed
perror( "fopen failed" ); // output reason for failure to open file
exit(EXIT_FAILURE );
} // end if
// implied else, fopen successful
// note: second pass through this loop will fail as file pointer will be at end of file
// always put literal first, so compiler will catch syntax errors
while(NULL != fgets(line,sizeof(line),file_ptr) )
{
if( EOF == fputs("df",file_ptr) )// appends 'df' to end of file
{ // then fputs failed
perror( "fputs failed" );
exit( EXIT_FAILURE );
}
// implied else, fputs successful
printf("2");
fflush( stdout );
} // end while
fclose(file_ptr);
return(0);
} // end function: main
Related
I am tried to use read/write/append modes with both fprintf and fscanf, and they'r not working. My The location of the folder where I've saved my file is ''C:\coding projects\test\Assignment Template'' and the name of the file is ''Assignment Template.txt''.
#include <stdio.h>
#include <stdlib.h>
int main()
{
char str[500];
FILE *ptr;
ptr=fopen("C:\coding projects\test\Assignment Template\Assignment template.txt","r");
fscanf(ptr,"%s",str);
return 0;
}
ps, I also tried to use the location of folder only without file name, also tried to use the name of the file only without folder location, but none of it seems to be working.
As pointed out by others, the several "single '\'" in the pathname to your file are wrong. You need to replace each "\" with "\\" OR with "/". Both these solutions would work.
The second idea is that "not working" is not a helpful diagnostic. To follow that with "nothing happened" does not supply any extra information.
Here is an example showing how to write this functionality so that you can at least understand where a problem might be occurring.
int main()
{
// Separate the filepath so it can be used in error message if necessary.
char *fname = "C:/coding projects/test/Assignment Template/Assignment template.txt";
char str[ 500 + 1 ]; // One extra for trailing '\0'
// temporary debugging report to confirm pathname is as expected.
printf( "Attempt open of '%s'\n", fname );
FILE *fp = fopen( fname, "r" );
// ALWAYS test return values for possible errors
if( fp == NULL ) {
// Able to report what went wrong
fprintf( stderr, "Failed to open %s\n", fname );
exit( EXIT_FAILURE );
}
// fscanf returns how many variables were 'satisfied'
// use that information
// Also, set a limit that won't overflow the buffer being filled
int num = fscanf( fp, "%500s", str );
// 'temporary' diagnostic "debugging" report to the console
printf( "Loaded %d items\n", num );
// clean up
fclose( fp );
return 0;
}
This is not "debugging with print statements"... To move forward developing code, one adds-or-modifies only a VERY few lines of code, then TESTS the consequences of those changes before adding/modifying a few more lines. "Incremental development". 'Testing' involves having clear expectations of what should happen and "seeing" if those expectations have been met. Had you printed the string that is the pathname of the file you want to open, you would have seen a problem before writing one more line of code. "Slowly and methodically, ALWAYS testing/checking."
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.
I want to open a file, read its contents, and then append a line to the file. I thought I should use the "a+" flag for the task.
I have a function which opens a file and returns a pointer to this file.
FILE* open_weekly_disk_file(char* filename){
FILE* weekly_log;
weekly_log = fopen(filename, "a+");
//weekly_log = fopen(filename, "r");
if(! weekly_log){
printf("The attempt to open the weekly log failed!\n");
return NULL;
} else{
return weekly_log;
}
}
Then I have a function which calls the function above and uses scanf to read contents from the file:
void sample_function(char* filename){
FILE* log;
char token[100], current_read[100];
int limit;
log = opened_weekly_disk_file(filename);
// The problem happens here
for(limit=0; limit < TOKEN_NUMBER; limit++){
if(fscanf(log, "%s%s", &token, ¤t_read) == 2){
printf("%s %s\n", token, current_read);
}
}
...
}
This code works when I use:
weekly_log = fopen(filename, "r");
But does not work when I change the "r" flag to "a+". I get a Segmentation fault right before the for loop.
That is because the mode spec "a" opens a file for appending, with the file pointer at the end. If you try to read from here, there is no data since the file pointer is at EOF. You should open with "r+" for reading and writing. If you read the whole file before writing, then the file pointer will be correctly positioned to append when you write more data.
If this is not enough, please explore ftell() and fseek() functions.
from this SO QA
from the man page:
a+
Open for reading and appending (writing at end of file). The file is
created if it does not exist. The initial file position for reading is
at the beginning of the file, but output is always appended to the end
of the file.
Answer:
There is just one pointer which initially is at the start of the file
but when a write operation is attempted it is moved to the end of the
file. You can reposition it using fseek or rewind anywhere in the file
for reading, but writing operations will move it back to the end of
file.
So, the problem is not the fact that the file is opened in append mode, because it is not, as far as reading from it is concerned.
The problem lies in what your code does in those three dots
log = opened_weekly_disk_file(filename);
...
The code quite probably writes to the file, making the file cursor move to the end of it before the reading occurs.
In the following C-code I open a file called test.txt which contains a few lines. I then read in those lines in a while-loop and print them to stdout. Afterwards I make a few changes in the file by e.g. appending the number 42 to it. I then want to print the contents of the changed file to stdout but I seem to missing something there. Here is my code so far (unnecessarily commented):
#include <stdio.h>
#include <stdlib.h> /* for exit() */
main ()
{ /* Declare variables */
FILE *file_read;
char file_save[100];
int number = 42;
/* Open file */
file_read = fopen("/home/chb/files/Cground/test.txt", "a+");
/* Check if file exists. */
if (file_read == NULL) {
fprintf(stderr, "Cannot open file\n");
exit(1);
}
/* Print what is currently in the file */
printf("This is what is currently in the file:\n");
while(fgets(file_save, 100, file_read) != NULL) {
printf("%s", file_save);
}
/* Change the contents of the file */
fprintf(file_read, "%d\n", number);
/* Print what is in the file after the call to fscanf() */
printf("This is what is now in the file:\n");
/* Missing code */
fclose(file_read);
}
It seems that a simple while-loop placed where Missing code is, similar to the one already used before will not suffice. Can someone please explain what is going on. I don't mind technicalities!
In order to read the file from the start again you have to call fseek() first, like so
fseek(file_read, 0, SEEK_SET);
this sets the stream position indicator back to the start of the file.
See http://www.cplusplus.com/reference/cstdio/fseek/ for more info.
You do not set the file pointer back to start. As it's already at the end of the file, there's nothing more to read.
Before reading the file again, do:
rewind(file_read); //before the "missing code" comment
to set it back at the start of the file.
I need to append a text at the end of each line in a file. I have the following code:
FILE *tmp_copy = tmpfile();
file = fopen ( argv[2], "rw" );
if ((file != NULL )) {
char line [ 128 ]; /* or other suitable maximum line size */
while( ( fgets ( line, sizeof line, file ) != NULL )) {
fputs(line, tmp_copy);
}
fclose ( file );
rewind(tmp);
char *p;
/* Reopen file now with write permissions */
fopen(argv[2], "w");
while (( p = fgets(line, 1024, tmp))!=NULL) {
//line[strlen(line)-1] = '\0'; /* Clear away newline */
//sprintf(line, "%s %s\n", line, more);
strcpy(line,"true");
//fputs(line, file);
}
fclose(file);
fclose(tmp);
}
}
I ve edited my code. still not working
but it's not working. why?
I'm guessing you want the line to be re-written to the file again. However, you are not writing to the file, just appending to the data in memory. It's also really not possible to read and write files at the same time like that, you have to do it in two steps:
Read from original file, appending wanted text to line, and write to temporary file
Rename (or copy) from temporary file to the original file
Edit: Pseudo-ish code for my answer:
original_file = fopen(original_file_name, "r");
temporary_file_name = tmpnam("dummy");
temporary_file = fopen(temporary_file_name, "w");
while (fgets(line, original_file))
{
remove_trailing_newline(line);
strcat(line, " TRUE\n");
fputs(line, temporary_file);
}
fclose(temporary_file);
fclose(original_file);
rename(temporary_file_name, original_file_name);
Unless the file is memory-mapped AND fgets returns a pointer to the original buffer (it doesn't. It needs to append the null.) AND strcat operates in place (it does), then you are not storing anything, only messing up some memory. Even if it did work, you would overwrite a part of the next line anyways.
You need to either
Write to a temporary file and rename that after you close the original one (as suggested by Joachim Pileborg).
Write into a buffer in memory and save it to the file when you are done reading.
Read the file into memory in one go, then start reading the buffer line by line.