This question already has answers here:
Append to the end of a file in C
(2 answers)
Closed 9 years ago.
I have a Linux C program that store configuration parameters in a text file. I read from the text file using
FILE *file = fopen(filename, "r"):
and write to the file using following code
FILE *file = fopen(filename, "w"):
I am getting a problem where the text file is cleared, it is blank the next time I come to read it.
I understand that when the file is opened for write it gets overwritten, my program stores the contents of the file after it has read it in and writes it back out.
It does in the main write it out correctly, but occasionally I will find that the text file is blank.
My initial thoughts were that this may be because the program was unsafely stopped, mid way through writing to the file leaving it blank or there were 2 instances of the program running and as one open it for writing, the other reads it in, meaning it would read in a blank file and then overwrite it with a blank file when it writes it out.
After some testing this does not seem to be the case.
This leaves me unsure as to what is causing the text file to be cleared.
Does anyone have any ideas?
See code below
char text_lines[200][54]; /* global variable */
void read_in_text_file()
{
/* this sub reads in the text file */
//printf("read in file\n");
/* declares the variables */
char line[128];
int counter = 0;
int length;
/* open a text file and read it in line by line, storing each line in a variable. also returning a value for the number of lines in each section */
static const char filename[] = "config.txt";
FILE *file = fopen(filename,"r"); /* opens the config file */
if (file==NULL){ /* checks if the file has successfully opened */
perror ("Error opening file"); /* displays error message on stderr - that returns reason file did not open */
printf("Error opening file\n"); /* tells the user that the file has not opened */
exit(0); /* exits the program if the text file can not be read in */
}
else{
//printf("the file is open\n");
while ( fgets ( line, sizeof line, file ) != NULL) /* reads each line of the text file */
{
sprintf(text_lines[counter],"%s",line); /* puts the line into a variable */
length = zstrlen(text_lines[counter]); /* calculates the length of the text not including \r or \n characters */
if(text_lines[counter][length-1] == '\n') /* checks if the last character is \n (a new line character) */
{
text_lines[counter][length-1] = '\0'; /* removes this new line character and replaces it with end of line identifier */
}
counter = counter + 1; /* uses a counter for each line */
} /* end of while loop */
number_of_lines = counter; /* puts the number of lines into a integer variable */
fclose(file); /* closes the file */
}
} /* end of sub for reading in the text file */
/* some changes may be made to the config before it is printed to the file again */
void print_to_text_file()
{
pthread_mutex_lock(&lock); /* block until thread has ownership */
/* sub for printing all the lines in the text_lines variable to the text file "config.txt" */
int counter;
static const char filename[] = "config.txt";
FILE *file = fopen(filename,"w"); /* opens the config.txt file, with write privileges */
if (file==NULL){ /* checks if the file has successfully opened */
perror ("Error opening file"); /* displays error message on stderr - that returns reason file did not open */
printf("Error opening file\n"); /* tells the user that the file has not opened */
}
else{
//printf("the file is open\n"); /* prints to the terminal screen the file has opened */
for (counter = 0; counter < number_of_lines; counter++) /* uses a for loop to scroll through all text lines */
{
// printf("%s\n",text_lines[counter]);
fprintf(file, "%s\n",text_lines[counter]); /* prints current text line to the file */
}
fclose(file); /* closes the file */
}
pthread_mutex_unlock(&lock); /* release blocking on thread */
} /* end of print text to file sub */
First, you should know that when you open a file with fopen and a mode of w, the file is immediately truncated to zero bytes (erasing its contents). You can use w+ or wa to prevent this.
Second, you might be missing an fclose() to close the file when you're done with it. Although when your program exits, all files are typically closed. However, if you don't close the file, changes made to it might not be committed to disk.
Since you updated the question to include some code, I can say that your program is unsafe and can overrun the text_lines buffer if the file contains more than 200 lines. If you can provide an actual complete but minimal test program that can be compiled and run, it would help you to get more answers next time.
Here is an actual program that can be compiled and run. It reads in the lines of config.txt, converts all characters to capitals, then writes the lines back out to the file. I removed your pthread mutex function calls since it was unnecessary in this test program (there's no multithreading).
#include <ctype.h> /* toupper */
#include <stdio.h> /* fopen, fclose, fgets, perror */
#include <stdlib.h> /* exit */
#include <string.h> /* strlen */
/* Constants */
/* Maximum number of lines to read */
#define TEXT_LINES_CAPACITY 54
/* Maximum length of a line including EOL and NUL */
#define MAX_LINE_LEN 200
/* Global variables */
char text_lines[TEXT_LINES_CAPACITY][MAX_LINE_LEN]; /* text of lines */
int number_of_lines; /* number of lines in text_lines */
/* Function declarations */
void capitalize_string(char* s);
void strip_eol(char* s);
void read_in_text_file(void);
void print_to_text_file(void);
/* Function definitions */
int main()
{
int i;
/* Read in the contents of the file. */
read_in_text_file();
/* Modify it by capitalizing the text. */
for (i = 0; i < number_of_lines; ++i)
{
capitalize_string(text_lines[i]);
}
/* Write out the modified contents to the same file. */
print_to_text_file();
return 0;
}
void capitalize_string(char* s)
{
while (*s != 0)
{
*s = toupper(*s);
++s;
}
}
/* Open a text file and read it in line by line. The lines are stored in the
* global variable text_lines and the number of lines in number_of_lines. */
void read_in_text_file(void)
{
static const char filename[] = "config.txt";
FILE *file = fopen(filename,"r"); /* opens the config file */
if (file == NULL)
{
/* Print error message after the file name. */
perror(filename);
exit(1); /* Exit with failure code (nonzero) */
}
else
{
/* Read each line of the text file. */
while (number_of_lines < TEXT_LINES_CAPACITY &&
fgets(text_lines[number_of_lines], MAX_LINE_LEN, file) != NULL)
{
strip_eol(text_lines[number_of_lines]);
++number_of_lines;
}
fclose(file);
}
}
/* Remove LF and/or CR characters from the end of the string. */
void strip_eol(char* s)
{
/* Loop while the string ends with a CR or LF character. */
while (strlen(s) > 0 &&
(s[strlen(s) - 1] == '\n' ||
s[strlen(s) - 1] == '\r'))
{
/* Erase the last character. */
s[strlen(s) - 1] = '\0';
}
}
/* Write all the lines of text_lines to the text file "config.txt" */
void print_to_text_file(void)
{
static const char filename[] = "config.txt";
/* open the config.txt file, with write privileges */
FILE *file = fopen(filename,"w");
if (file == NULL)
{
/* Print error message after the file name. */
perror(filename);
}
else
{
int i;
/* Iterate over all text lines. */
for (i = 0; i < number_of_lines; i++)
{
fprintf(file, "%s\n", text_lines[i]); /* prints current text line to the file */
}
fclose(file); /* closes the file */
}
}
Related
This is my first time asking on Stack Overflow, i'll try my best to make a good question.
Fell free to correct me if i miss relevant information or stuff like that.
I'm writting a little program that creates a simple options menu.
My plan consist in very few steps:
Read file names from that macro #define file_dir "/home/me/dir"
Store that file names into names.txt.
I have to display the content of names.txt as options in my simple menu.
At the moment i was able to accomplish two of three steps but not so well i guess.
I create 2 function to do these 2 jobs. create_file(), read_file(), respectively.
Now is where my question really begins:
Each function works ok when i execute isolated. If i call as it intended to be
The second function read_file() instead to print the content of the file to stdout
it rewrite the names.txt and put a "square" character at the end of the file.
My plan is to redirect the return of the read_file() to an array.
So i can display as options in this bare bone menu.
Please help me understand.
Why i can't use this two functions like that ?
I know i am new to C and this program is far from be complete.
Here's my code:
#include <stdio.h>
#include <dirent.h>
#include <unistd.h>
#define my_dir "/home/me/dir"
int roms_list;
int create_list()
{
/* redirect stdout to a file */
freopen("names.txt", "a+", stdout);
/* open dir and print their content */
DIR *dir;
struct dirent *ent;
if ((dir = opendir (nes_dir)) != NULL)
{
while ((ent = readdir (dir)) != NULL)
{
printf ("%s\n", ent->d_name);
}
}
closedir(dir);
close(names.txt);
}
int read_list()
{
FILE * list;
char ch;
list = fopen("names.txt", "r+");
if(NULL == list)
{
printf("file cant' be opened \n");
return 1;
}
do
{
ch = fgetc(list);
printf("%c", ch);
}
while (ch != EOF);
fclose(list);
}
int main()
{
create_list();
read_list();
return 0;
}
As MikeCAT points out, you attempt to printf("%c", ch); before checking ch != EOF resulting in attempting to print the int EOF values with the %c conversion specifier resulting in Undefined Behavior due to the mismatch in argument type and conversion specifier. ch must be type int to match the return type of fgetc() and to make a valid comparison with EOF.
If a conversion specification is invalid, the behavior is undefined.
If any argument is not the correct type for the corresponding
conversion specification, the behavior is undefined.
C11 Standard - 7.21.6.1(p9)
Additional Areas Where Your Code Needs Improvement
Your create_list() function is type int, but fails to return any value. Since create_list() can succeed or fail, it is imperative that the return type be able to communicate whether it succeeded or failed. Type int is fine, you can for example return 0; on a failure to read or on success, return the number of entries written to the file;
Your read_list() function is simply an output function that outputs the contents of the file written. While it can succeed or fail, it isn't critical to the continued operation of your program. Choosing type void for an output function is fine.
Do not hardcode file or directory names in functions. You shouldn't have to recompile your program just to read from a different directory or write to a different filename. Pass the directory to read and the filename to write as arguments to your program. That is what the arguments to main() are for, e.g. int main (int argc, char **argv). (or prompt the user to input both string values)
open your file in main() once and on successful open, pass a FILE* pointer for the open file stream to each of your functions as a parameter. You validate the open in main() because there is no need to call either function if fopen() fails.
pass the directory name to read to create_list() as a const char * parameter.
condition your call to read_list() on a successful return from create_list(). If create_list() fails, there is no need to call read_list().
Putting the improvements together, you could do something similar to the following:
#include <stdio.h>
#include <dirent.h>
/* returns 0 on failure, no. of files written on success */
int create_list (FILE *fp, const char *dname)
{
/* open dir and print their content */
DIR *dir;
struct dirent *ent;
int n = 0; /* simple counter for no. of entries read */
if ((dir = opendir (dname)) == NULL) { /* return 0 on failure to open */
return 0;
}
while ((ent = readdir (dir)) != NULL) {
/* skip dot files */
if ((ent->d_name[0] == '.' && !ent->d_name[1]) ||
(ent->d_name[0] == '.' && ent->d_name[1] == '.')) {
continue;
}
fprintf (fp, "%s\n", ent->d_name);
n++; /* increment counter */
}
closedir(dir);
return n; /* return the number of enteries written */
}
/* read list can be type void - it simply outputs contents of file */
void read_list (FILE *fp)
{
int ch; /* must be int */
while ((ch = fgetc (fp)) != EOF) { /* read char, validate not EOF */
putchar (ch); /* write to stdout */
}
}
int main (int argc, char **argv) {
char *dname, *fname; /* dirname and filename pointers */
int nfiles = 0; /* no. of files written */
FILE *fp = NULL; /* file pointer */
if (argc != 3) { /* validate 2 arguments given (dirname filename) */
fputs ("error: dirname and filename required\n"
"usage: ./program \"/path/to/files\" \"filename\"\n", stderr);
return 1;
}
dname = argv[1]; /* assign arguments to give descriptive names */
fname = argv[2]; /* (you could just use argv[x], a name helps) */
fp = fopen (fname, "w+"); /* open file for reading/writing */
if (!fp) { /* validate file open for reading/writing */
perror ("file open failed");
return 1;
}
/* validate create_list succeeds */
if ((nfiles = create_list (fp, dname))) {
printf ("%d files:\n\n", nfiles); /* number of entries in file */
rewind (fp); /* rewind file pointer */
read_list (fp); /* read list */
}
if (fclose (fp) != 0) { /* always validate close-after-write */
perror ("fclose fp");
}
}
Example Use/Output
You provide the directory to read as the first argument and the filename to write as the second. ./progname /path/to/read /file/to/write
A short example:
$ ./bin/dirlist_names ./km dat/dnames.txt
47 files:
startstop.o
kernelmod_hello1.c
.chardev.o.cmd
hello-4.o
.hello-2.mod.cmd
hello-2.mod
<snip>
hello-5.mod
.startstop.o.cmd
.hello-4.mod.cmd
chardev.mod
Makefile
hello-2.c
It looks like you are printing EOF. You should check if ch is EOF before printing that.
Also fgetc() returns int and convering the return value to char will prevent it from distinguishing EOF from one of valid byte, so you should use int instead of char for ch.
Instead of this:
char ch;
/* ... */
do
{
ch = fgetc(list);
printf("%c", ch);
}
while (ch != EOF);
You should use:
int ch;
/* ... */
while ((ch = fgetc(list)) != EOF)
{
printf("%c", ch);
}
Or:
int ch;
/* ... */
ch = fgetc(list);
while (ch != EOF)
{
printf("%c", ch);
ch = fgetc(list);
}
I have a C-program built on macOS High Sierra using gcc and a makefile. The first thing the program does is read a binary inputfile. The filename can be either specified from the terminal command line together with the shell command or the program will ask for it when it’s not specified.
My issue is that when the input file is not specified together with the shell command, the program returns an error, saying it cannot open the file.
Here’s what works and what doesn’t:
(program and input file are in the same directory)
open terminal
from the command line type:
./program_name –i input.dat
=> works fine
open terminal
from the commandline type:
./program_name
program prompts:
Inputfile:
I type: input.dat
=> error opening file
open Finder and go to directory with program and input file
doubleclick on program_name icon
program starts in terminal and prompts:
Inputfile:
I type: input.dat
=> error opening file
I run the very same source code on linux and windows where it works ok, so I think it must be an OS thing that I don't understand?
I can add that the program was untrusted because it doesn't come from the app store. CTRL-click on the icon solved that.
--EDIT - sorry for not adding the verifyable code.
To clarify: the argc/argv part works fine. it's the last section of the routine where it prompts for the file name where it goes wrong. Maybe it's indeed the path as Jabberwocky suggested. I'll check on that tonight and will follow-up here.
void GetFileName(nr_args, args, filename, json)
int nr_args;
char **args;
char *filename;
int* json;
{
int i = 1;
filename[0] = '\0';
/* the command 'interpreter' itself is stored in argv[0] */
while (i<nr_args) {
if (strcmp(args[i], "-e") == 0) {
/* we cannot set the json flag here, because */
/* flags have not been initialized yet */
*json = 1;
i++;
}
else {
if (strcmp(args[i], "-i") == 0) {
if (nr_args > i+1) {
/* inputfile was specified */
strncpy(filename, args[++i], MAX_ID_LENGTH);
i++;
}
}
else {
PrintError(41, NULL, args[i]);
i++;
}
}
}
if (filename[0] == '\0') {
printf("\n\nInputfile: ");
scanf("%19s", filename);
filename[MAX_ID_LENGTH] = '\0';
/* clear the input buffer, to prevent parsing an */
/* empty string as the first user command */
/* always do a getchar() independent of OS */
getchar();
printf("\n");
}
}
And this is the part where the file is opened (from main() )
/* Get filename */
GetFileName(argc, argv, inputfile, &json);
/* Open the datafile */
if ((datafile = fopen(inputfile, "rb")) == NULL) {
PrintError(40, NULL, inputfile);
ExitProgram();
return(OK);
}
EDIT2-- As per Andrew Henle's reply, this is the prototype.
void GetFileName(int, char**, char*, int*);
The function is called from the same file as it is defined in.
I run the very same source code on linux and windows where it works ok
That is meaningless.
This is an old K&R-style function definition:
void GetFileName(nr_args, args, filename, json)
int nr_args;
char **args;
char *filename;
int* json;
{
...
You can not call that function safely if the calling code uses a function prototype. A K&R-defined function expects all of its arguments to have underdone default argument promotion. A prototype for the function means the caller won't perform those promotions. The mismatch will result in undefined behavior.
Don't use such ancient functions. You can't use them with a prototype, and without a prototype you have no type safety in the function call.
Use a proper, C standard-compliant function definition:
void GetFileName( int nr_args, char **args, char *filename, int* json )
{
...
And then provide a proper prototype for all calls to the function.
I made the following routine that works for me.
So, as an example:
if the program is in location /usr/mac/my-name/programs/
and the input filename that was entered is input.dat
Then this routine will return: /usr/mac/my-name/programs/input.dat
Thanks for all your help.
#include <sys/param.h> /* realpath() */
#include <limits.h> /* PATH_MAX */
#include <mach-o/dyld.h> /* _NSGetExectablePath() */
char *GetFullPath(const char*);
#define MAX_FILENAME_LEN 100 /* or another value */
char *GetFullPath(const char *filename)
{
char path_buf[PATH_MAX + 1];
char resolved_name[PATH_MAX + 1];
char *real_path;
char *return_path;
uint32_t buf_size = sizeof(path_buf);
int index = 1;
/* this functions returns the full path of the current */
/* running application appended with var 'filename' at */
/* the end. In case of an error it returns NULL. */
if ((return_path = (char *) malloc(MAX_FILENAME_LEN)) == NULL) {
printf("GetFullPath(): error in Malloc()\n");
return(NULL);
}
/* get relative path */
if (_NSGetExecutablePath(path_buf, &buf_size) != 0) {
/* buffer too small */
printf("File Path too long.");
free(return_path);
return(NULL);
}
/* convert to absolute path */
if ( (real_path = realpath(path_buf, resolved_name)) == NULL) {
printf("Could not determine path.\n");
free(return_path);
return(NULL);
}
/* strip the application name from the end of the path */
index = strlen(real_path) - 1;
while (real_path[index] != '/') {
index--;
}
/* now check if there's enough room in return_path */
if (strlen(real_path) + strlen(filename) >= MAX_FILENAME_LEN) {
printf("File path too long.\n");
free(return_path);
return(NULL);
}
/* there's enough room, copy path and filename to return_path */
strncpy(return_path, real_path, index+1);
/* do not try to free() real_path */
return_path[index+1] = '\0';
strncat(return_path, filename, strlen(filename));
return(return_path); /* caller must free() return_path */
}
I am writing a program in C ..that opens a plain text file containing C-like source code, reads it, and outputs a file with the same content as the first, except that all comments are removed.The program must check that all brackets match,if they do not, the program should display an error message,at showing the type of error and the line number where this error was encountered.(I displayed an error message but how can i locate the error position..?) The input and output files are passed to the program a####nd line parameters, as in:
./your_executable inputfile.txt outputfile.txt
Here is the code that i wrote:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* Functions */
void check_comment (char) ; // checks for both types of comments, then passes on control to below comments
void block_comment () ; // handles block or multiline comments
void single_comment () ; // handles single line comments
/* 2 file pointers - 1st is for the file in which we check for comments,
and 2nd is the file in which we copy the code after removing comments */
FILE *fp , *fp2;
int main(void)
{
char c;
fp = fopen ("inputfile.txt","r") ; // open the first file in read mode
fp2 = fopen ("outputfile.txt","w") ; // open the second file in write mode
while((c=fgetc(fp))!=EOF) // read the file character by character
check_comment(c); // check for each character if it seems like the beginning of a comment
// close both the files at the end of the program
fclose(fp);
fclose(fp2);
FILE *fp;
char fname[20];
char brackets[20] = "{}[]()";
int bracketCounts[6] = {0};
char * found;
int i;
printf("Please enter the destination of the file: \n");
scanf("%s", fname);
if ((fp = fopen(fname, "r")) == NULL){
printf("Problem opening file!\n");
return 0x00;
}
printf("File opened correctly\n");
// counting various parentheses
while ((c = getc(fp)) != EOF){
found = strchr(brackets, c);
if (found != NULL) {
bracketCounts[found - brackets]++;
}
}
// dont't forget to close file after reading is done
fclose(fp);
// checking parentheses counters
for (i=0; i < 6; i+=2) {
if (bracketCounts[i] != bracketCounts[i+1]) {
printf("Unbalanced parentheses !\n");
return 0x00;
}
}
printf("All parentheses are OK!\n");
return 0;
}
// function that handles both types of comments
void check_comment(char c)
{
char d;
if( c == '/') // if the character starts with '/', it 'could' be a comment
{
if((d=fgetc(fp))=='*') // if the next character we read is '*', it is the beginning of multiblock comment
block_comment(); // pass control to function that handles multiblock comments
else if( d == '/') // else if the next character we read is '/', it is the beginning of single line comment
{
single_comment();// pass control to function that handles single line comment
}
else
{
// if both the cases fail, it is not a comment, so we add the character as it is in the new file.
fputc(c,fp2);
fputc(d,fp2);
}
}
// again, if all above fails, we add the character as it is in the new file.
else
fputc(c,fp2);
}
// function that handles block comments
void block_comment()
{
char d,e;
while((d=fgetc(fp))!=EOF) // the block comment has started, read the character by character
{
/* keep reading the characters and do nothing,
as they do not have to be copied into the new file (we are removing the comments)
*/
if(d=='*') // if the comment 'seems' like ending
{
e=fgetc(fp); // check if it actually ends (block comments end with '*/')
if(e=='/') // if the comment 'has' ended, return from the function
return;
}
}
}
// function that handles single line comments
void single_comment()
{
char d,e;
while((d=fgetc(fp))!=EOF) // the single line comment has started, read the character by character
{
/* keep reading the characters and do nothing,
as they do not have to be copied into the new file (we are removing the comments)
*/
if(d=='\n') // check if the comment ends (single comments end with '\n', or newline)
return; // if the comment 'has' ended, return from the function
}
}
you can implement or use stack data structure, while reading input file if you get { , ( or [ push on the stack and if you get }, ) or ] pop the stack,at the end of input file stack should be empty then you get proper match else some mismatch happened.
along with paranthesis you can keep line numbers(or position, etc)
ex: 1 (
2 (somethig)
3 something),
push (, line1, then push (, line2 and when you get ) pop (, line2
and so on, in this case if you don't get second closing ), you can say that (, line1
is missing closing.
Example:
Three files
hi.txt
Inside of txt: "May we be"
again.txt
Inside of txt: "The ones who once"
final.txt
Inside of txt: "knew C"
And then, another file called "order"
order.txt
Inside of txt:
"hi.txt;6"
"again.txt;7"
"final.txt;3"
What I want: read the first file name, open it, list the content, wait 6 seconds, read the second name, open it, list the content, wait 7 seconds, read the third name, open it, list the content, wait 3 seconds.
If I do it without opening the content (you'll see a second while on my code) and list the names, it works, yet for some reason it doesn't when it's about the content.
orderFile = fopen("order.txt","r");
while(fscanf(orderFile,"%49[^;];%d",fileName,&seconds) == 2)
{
contentFile = fopen(fileName,"r");
while(fscanf(contentFile,"%[^\t]",textContent) == 1)
{
printf("%s\n", textContent);
}
sleep(seconds);
fclose(contentFile);
}
fclose(orderFile);
Output:
May we be
(Waits 7 seconds)
Program closes with "RUN SUCCESSFUL"
EDIT#
It works now, as you guys said, this was the problem:
Old:
while(fscanf(orderFile,"%49[^;];%d",fileName,&seconds) == 2)
New:
while(fscanf(orderFile," %49[^;];%d",fileName,&seconds) == 2)
I'm having a "hard" time to completely understand it, what does the space does? doesn't accept enters? spaces? What exactly is it?
Don't use fscanf for that
int
main()
{
FILE *orderFile = fopen("order.txt", "r");
if (orderFile != NULL)
{
int seconds;
char line[128];
/*
* fgets, read sizeof line characters or unitl '\n' is encountered
* this will read one line if it has less than sizeof line characters
*/
while (fgets(line, sizeof line, orderFile) != NULL)
{
/*
* size_t is usually unsigned long int, and is a type used
* by some standard functions.
*/
size_t fileSize;
char *fileContent;
FILE *contentFile;
char fileName[50];
/* parse the readline with scanf, extract fileName and seconds */
if (sscanf(line, "%49[^;];%d", fileName, &seconds) != 2)
continue;
/* try opening the file */
contentFile = fopen(fileName,"r");
if (contentFile == NULL)
continue;
/* seek to the end of the file */
fseek(contentFile, 0, SEEK_END);
/*
* get current position in the stream,
* it's the file size, since we are at the end of it
*/
fileSize = ftell(contentFile);
/* seek back to the begining of the stream */
rewind(contentFile);
/*
* request space in memory to store the file's content
* if the file turns out to be too large, this call will
* fail, and you will need a different approach.
*
* Like reading smaller portions of the file in a loop.
*/
fileContent = malloc(1 + fileSize);
/* check if the system gave us space */
if (fileContent != NULL)
{
size_t readSize;
/* read the whole content from the file */
readSize = fread(fileContent, 1, fileSize, contentFile);
/* add a null terminator to the string */
fileContent[readSize] = '\0';
/* show the contents */
printf("%s\n", fileContent);
/* release the memory back to the system */
free(fileContent);
}
sleep(seconds);
fclose(contentFile);
}
fclose(orderFile);
}
return 0;
}
Everything is barely explained in the code, read the manuals if you need more information.
I'm trying to write a program to swap a character that I would specify on the command line (a command line argument) with a character in the input text file. The first command line argument is the character I want to change, the second argument is character that I want to replace the old character with, and the third argument is the input file.
When I do this, my program should generate an output file named: "translation.txt". I know that the problem with my program is in the "if" statements/the fprintf statements, but I'm not sure how to fix this. I was thinking of reading each character in the input file separately, and from there, I wanted to use "if" statements to determine whether or not to replace the character.
void replace_character(int arg_list, char *arguments[])
{
FILE *input, *output;
input = fopen(arguments[3], "r");
output = fopen("translation.txt", "w");
if (input == NULL)
{
perror("Error: file cannot be opened\n");
}
for (int i = 0; i != EOF; i++)
{
if (input[i] == arguments[1])
{
fprintf(output, "%c\n", arguments[2]);
}
else
{
fprintf(output, "%c\n", arguments[1]);
}
}
}
int main(int argc, char *argv[])
{
if (argc < 5)
{
perror("Error!\n");
}
replace_character(argc, argv);
}
Okay I think this can help:
#include <stdio.h>
int main(int argc, char** argv)
{
if (argc < 4) return -1; /* quit if argument list not there */
FILE* handle = fopen(argv[3], "r+"); /* open the file for reading and updating */
if (handle == NULL) return -1; /* if file not found quit */
char current_char = 0;
char to_replace = argv[1][0]; /* get the character to be replaced */
char replacement = argv[2][0]; /* get the replacing character */
while ((current_char = fgetc(handle)) != EOF) /* while it's not the end-of-file */
{ /* read a character at a time */
if (current_char == to_replace) /* if we've found our character */
{
fseek(handle, ftell(handle) - 1, SEEK_SET); /* set the position of the stream
one character back, this is done by
getting the current position using
ftell, subtracting one from it and
using fseek to set a new position */
fprintf(handle, "%c", replacement); /* write the new character at the new position */
}
}
fclose(handle); /* it's important to close the file_handle
when you're done with it to avoid memory leaks */
return 0;
}
Given an input specified as the first argument, it will seek a character to replace and then replace it with what is stored in replacement. Give it a try and let me know if it doesn't work. I run it like this:
./a.out l a input_trans.txt
My file has just the string 'Hello, World!'. After running this it's changed to 'Heaao, Worad!'.
Read up on ftell and fseek, as they're key here for what you need to do.
EDIT: Forgot to add an fclose statement that closes the file handle at the end of the program. Fixed!