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);
}
Related
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 */
}
In c code. I have an input file (called in) that is a mad-lib in the format, "I have really < adjective> eyes" (no spaces inside the <>) and I want to write a bool function that uses scanf to read every word and return true if the word begins with '<' (also called a token) How would I go about doing that? and yes I have to use scanf. Here is what I have right now but I do not think that it is completely right, so another question is, how do I know if my function is properly working.
/* istoken = returns true if word is a token */
bool istoken(char word[]) {
char first;
int firstindex;
while (1) {
scanf("%s", word);
first = word[MAX_LEN];
firstindex = (int)strlen(word);
if (first == '<') {
printf("The token is: %s\n", first);
return true; }
else {
return false; }
}
}
In the caller, word must be sufficienly sized to hold the largest word in your text (+3 chars, 2 for <,> and the nul-termanting character. You should pass the maximum length for word as a parameter to istoken, but since you are using scanf, you must hard-code the field width modifier to protect your array bounds. (that is one of the reasons fgets is recommended over scanf -- but you must use scanf). Don't skimp on buffer size for word in the caller. Something like the following should suffice in the caller (probably main() for you):
#define MAXC 1024
...
char word[MAXC] = "";
There is no need for first or firstindex. To check the first character in a string, all you need do is dereference the pointer. With that, it is simply a matter of:
/* istoken = returns true if word is a token */
bool istoken (char *word) {
while (scanf("%1023s", word) == 1) /* did a valid read take place? */
if (*word == '<') /* is 1st char '<' ? */
return true; /* return true */
return false; /* out of words, return false */
}
(note: simply returning the token in word via the pointer parameter while returning bool, seems a bit of an awkward factoring of your code -- but it is doable. Also, if the token exceeds 1024 chars, including the nul-terminating char -- you will not have a complete token in word on function return)
Look things over and let me know if you have further questions.
A Short Example Reading stdin
#include <stdio.h>
#include <stdbool.h>
#define MAXC 1024
/* istoken = returns true if word is a token */
bool istoken (char *word) {
while (scanf("%1023s", word) == 1) /* did a valid read take place? */
if (*word == '<') /* is 1st char '<' ? */
return true; /* return true */
return false; /* out of words, return false */
}
int main (void) {
char word[MAXC] = "";
if (istoken (word))
printf ("found token: '%s'\n", word);
else
fprintf (stderr, "error: no token found.\n");
return 0;
}
Example Use/Output
$ echo "my dog has <too> many fleas." | ./bin/scanftoken
found token: '<too>'
Last note: while you, as you propose in the comment below, can output the token from within intoken, e.g.
bool istoken(char word[]) {
while (scanf("%100s", word) == 1) {
if (word[0] == '<') {
printf("the token is: %s\n", word);
return true;
}
}
return false;
}
That is generally something you want to avoid. Within your program design you want (as a goal) to separate your implementation (what your program does, computes, etc..) from Input/Output. That makes your code usable when called by more than one function that wants to output printf("the token is: %s\n", word);
While a bit uncommon, your istoken function that locates a token and returns true/false makes more sense if the caller then uses that return to determine what to do with the token in word. If you are just going to print it from inside intoken if a token is found, and then do nothing with the return in the caller, then why declare it as bool anyway -- you may as well just declare it as void if you are not using the return.
Like I said this is (a goal). You can factor your code any way you like as long as it is valid code. The use of printf within istoken is perfectly valid for temporary debugging purposes as well. (in fact that is one of the most helpful debugging tools you have, just sprinkle temporary printf statements throughout the logic path in your program to find out where you code works as intended and where the "train-falls-off-the-track" so to speak.
Example with File I/O
OK, we are finally getting to 'Z' with this 'XY' problem. Since, as I now understand, you have your text in a file (I have used "myfile.txt" for the input) and you want to read your inputfile in istoken and return word and true/false to main() and if true then write the token to your output file (I used "tokenfile.txt" before for my output file), then what you need to do is open both your input file and output file using fopen in main() similar to the following:
FILE *ifp = fopen ("myfile.txt", "r"), /* infile pointer */
*ofp = fopen ("tokenfile.txt", "w"); /* outfile pointer */
(I'm not that creative, I just use ifp for the input file pointer and ofp for the output file pointer)
Whenever you open a file, before you attempt to read or write to the file, you must validate that the file is actually open for reading or writing (e.g. fopen succeeded). For example:
if (ifp == NULL) { /* validate input open for reading */
perror ("fopen-myfile.txt");
return 1;
}
if (ofp == NULL) { /* validate output open for writing */
perror ("fopen-tokenfile.txt");
return 1;
}
Now with both files open, you can call istoken and read from ifp. However, this takes modifying istoken to take a FILE * parameter for use with fscanf instead of using scanf. For example:
/* istoken = returns true if word is a token */
bool istoken (FILE *ifp, char *word) {
while (fscanf(ifp, "%1023s", word) == 1) /* valid read take place? */
if (*word == '<') /* is 1st char '<' ? */
return true; /* return true */
return false; /* out of words */
}
After the return of istoken, you can write to stdout to let the user know if a token was found and also write to ofp to store token in your output file, e.g..
if (istoken (ifp, word)) { /* call istoken passing open ifp */
printf ("found token: '%s'\n", word); /* output token */
fprintf (ofp, "%s\n", word); /* write token to outfile */
}
else
fprintf (stderr, "error: no token found.\n");
Lastly, you must fclose the files you have open. But there is a twist for files you write to. You should validate the fclose to insure a stream-error did not occur on ofp that may not have been otherwise caught. e.g.
fclose (ifp); /* close infile pointer */
if (fclose(ofp) == EOF) /* validate "close-after-write" */
perror ("stream error on outfile stream close");
Putting it altogether, you can do something like the following:
#include <stdio.h>
#include <stdbool.h>
#define MAXC 1024
/* istoken = returns true if word is a token */
bool istoken (FILE *ifp, char *word) {
while (fscanf(ifp, "%1023s", word) == 1) /* valid read take place? */
if (*word == '<') /* is 1st char '<' ? */
return true; /* return true */
return false; /* out of words */
}
int main (void) {
char word[MAXC] = "";
FILE *ifp = fopen ("myfile.txt", "r"), /* infile pointer */
*ofp = fopen ("tokenfile.txt", "w"); /* outfile pointer */
if (ifp == NULL) { /* validate input open for reading */
perror ("fopen-myfile.txt");
return 1;
}
if (ofp == NULL) { /* validate output open for writing */
perror ("fopen-tokenfile.txt");
return 1;
}
if (istoken (ifp, word)) { /* call istoken passing open ifp */
printf ("found token: '%s'\n", word); /* output token */
fprintf (ofp, "%s\n", word); /* write token to outfile */
}
else
fprintf (stderr, "error: no token found.\n");
fclose (ifp); /* close infile pointer */
if (fclose(ofp) == EOF) /* validate "close-after-write" */
perror ("stream error on outfile stream close");
return 0;
}
Example Input File
$ cat myfile.txt
my dog has <too> many fleas.
Example Use/Output
$ ./bin/scanftoken
found token: '<too>'
$ cat tokenfile.txt
<too>
The best advice I can give you on learning C is to simply slow down. There is a lot to learn, and in fact given 30 years, I have barely scratched the surface (that and they keep revising the standard every so often). Just take it a step at a time. Loop up the man page for each function you use, find out what the proper parameter are and most critically what it returns and what form of error reporting is has (e.g. does it set errno so you can use perror to report the error or do you need to use fprintf (stderr, ....)?
Always enable compiler warnings and read and understand the warning and do not accept code until it compiles without warning. You can learn a lot of C just by listening to what your compiler is telling you. And if all else fails... talk to the duck. How to debug small programs, really, it helps :)
If you mind using some powerful lexical analyzer, I suggest you using flex which can help you lot for tokenization.
As you can see, Flex let you write token pattern and generate a C parser which does all the work.
Here is a program which compresses multiple blanks and tabs down to a single blank, and throws away whitespace found at the end of a line:
%%
[ \t]+ putchar( ' ' );
[ \t]+$ /* ignore this token */
You can find more at http://alumni.cs.ucr.edu/~lgao/teaching/flex.html.
I'm currently working on this assignment and I'm stuck. The objective is to read a file and find if these char values exist in the String from the file. I have to compare a String from a file to another String I put in as an argument. However, just as long as each char value is in the String from the file then it "matches".
Example (input and output):
./a.out file1 done
done is in bonehead
done is not in doggie
Example (file1):
bonehead
doggie
As you can see the order in which is compares Strings does not matter and the file also follows one word per line. I've put together a program that finds if the char value is present in the other String but that is only part of the problem. Any idea how to go about this?
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv){
FILE *f = fopen(argv[1], "r");
char *line = NULL;
size_t len = 0;
ssize_t read;
char *word = argv[2];
if(argc != 3){
printf("./a.out <file> <word>\n");
exit(EXIT_SUCCESS);
}
if(f == NULL){
printf("file empty\n");
exit(EXIT_SUCCESS);
}
// confused what this loop does too
while((read = getline(&line, &len, f)) != -1){
char *c = line;
while(*c){
if(strchr(word, *c))
printf("can't spell \"%s\" without \"%s\"!\n", line, word);
else
printf("no \"%s\" in \"%s\".\n", word, line);
c++;
}
}
fclose(f);
exit(EXIT_SUCCESS);
}
Another approach would simply keep a sum of each character matched in the line read from the file, adding one for each unique character in the word supplied to test, and if the sum is equal to the length of the string made up by the unique characters is the search term, then each of the unique characters in the search term are included in the line read from the file.
#include <stdio.h>
#include <string.h>
#define MAXC 256
int main (int argc, char **argv) {
if (argc < 3 ) { /* validate required arguments */
fprintf (stderr, "error: insufficient input, usage: %s file string\n",
argv[0]);
return 1;
}
FILE *fp = fopen (argv[1], "r");
char line[MAXC] = "";
char *s = argv[2]; /* string holding search string */
size_t slen = strlen(s), sum = 0, ulen;
char uniq[slen+1]; /* unique characters in s */
if (!fp) { /* validate file open */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
memset (uniq, 0, slen+1); /* zero the VLA */
/* fill uniq with unique characters from s */
for (; *s; s++) if (!strchr (uniq, *s)) uniq[sum++] = *s;
ulen = strlen (uniq);
s = argv[2]; /* reset s */
while (fgets (line, MAXC, fp)) { /* for each line in file */
if (strlen (line) - 1 < ulen) { /* short line, continue */
printf ("%s is not in %s", s, line);
continue;
}
char *up = uniq; /* ptr to uniq */
sum = 0; /* reset sum */
while (*up) if (strchr (line, *up++)) sum++; /* count chars */
if (sum < ulen) /* validate sum */
printf ("%s is not in %s", s, line);
else
printf ("%s is in %s", s, line);
}
fclose (fp); /* close file */
return 0;
}
Example Use/Output
$ ./bin/strallcinc dat/words.txt done
done is in bonehead
done is not in doggie
which would work equally well for duplicate characters in the search string. e.g.
$ ./bin/strallcinc dat/words.txt doneddd
doneddd is in bonehead
doneddd is not in doggie
You can decide if you would handle duplicate characters differently, but you should make some determination on how that contingency will be addressed.
Let me know if you have any questions.
confused what this loop does
The while (read ... line obviously reads in lines from your file, placing them in the line variable
*c is a pointer to the start of the variable line and this pointer is incremented by c++, so that each letter in the word from the file is accessed. The while loop will be terminated when *c points to the null terminator (0).
The if (strchr(word ... line is testing if the test word contains one of the letters from the word in the file.
This seems to be the reverse of what you are trying to do - finding if all the letters in the test word can be found in the word from the file.
The printf lines are not sensible because there is no either/or - you need one line to print 'yes' our letters are present and one line to print 'no' at least one letter is not present.
The printf statements should be outside the comparison loop, so that you don't get multiple lines of output for each word. Add a flag to show if any letter does not exist in the word. Set flag to 1 at start, and only change it to 0 when a letter is not present, then use the flag to print one of the two outcome statements.
This code snippet may help
/* set flag to 'letters all present' */
int flag = 1;
/* set pointer c to start of input line */
c = word;
/* test word from file for each letter in test word */
while(*c) {
if(strchr(line, *c) == NULL) {
/* set flag to letter not present */
flag = 0;
break;
}
c++;
}
got some code here that won't compile correctly because it is saying that my pointer is already null when i am testing for a not null expression in my main function. here is the code :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXCODE 53
#define MAXMESSAGE 256
void getCode(char *codeIn, char *filename) {
FILE *codeFile;
/* Open the file with the code */
codeFile = fopen(filename, "r");
if (codeFile == NULL) {
printf("Error opening the code file - program terminated\n");
exit(1);
}
/* Read the first (and assumed only) line from the file */
fgets(codeIn, MAXCODE, codeFile);
/* Terminate the string with /0 */
codeIn[MAXCODE] = '\0';
/* Close the file */
fclose(codeFile);
return;
}
int getMessage(int *message, char *filename) {
FILE *messageFile;
int counter = 0;
/* Open the file with the message */
messageFile = fopen(filename, "r");
if (messageFile == NULL) {
printf("Error opening the message file - program terminated\n");
exit(1);
}
/* Read one number at a time from the file and store it */
while (!feof (messageFile))
{
fscanf (messageFile, "%d", (message+counter));
counter++;
}
/* Close the file */
fclose(messageFile);
return (counter);
}
void sortMessage(int *message, int size) {
int i, j, temp;
for (i=0; i<size-1; i++) {
for (j=i; j<size; j++) {
if (message[i]>message[j]) {
temp = message[i];
message[i] = message[j];
message[j] = temp;
}
}
}
return;
}
void decodeMessage(char *codeIn, int *message, int size) {
FILE *outputFile;
int i = 0;
/* Open the output file */
outputFile = fopen("csis.txt", "w");
if (outputFile == NULL) {
printf("Error opening the output file - program terminated\n");
exit(1);
}
for (i=0; i< size; i++) {
fprintf(outputFile, "%c", codeIn[message[i]%100]);
printf("%c", codeIn[message[i]%100]);
}
printf("\n");
/* Close the file */
fclose(outputFile);
return;
}
int main(int argc, char *argv[])
{
char code[MAXCODE];
int msg[MAXMESSAGE];
int msgSize;
if (argc != 3) {
printf("This program takes two arguments: the name of the file with the code, and the name of the file with the encoded message\n");
}
getCode(code, argv[1]);
msgSize = getMessage(msg, argv[2]);
sortMessage(msg, msgSize);
decodeMessage(code, msg, msgSize);
return;
}
So basically my code is using two files called codefile.txt and msgfile.txt to decode the secret message and write the decoded sequence to a new text file called csis.
As woolstar pointed out in the comments, you don't need to NUL terminate your codeIn array following fgets, because fgets will do that for you. In fact, this constitutes an overflow which we can best see by considering what happens when MAXCODE is 1: codeIn contains only one element: codeIn[0], and accessing codeIn[1] is an error.
Similarly, since MAXCODE is 53 and that's how many elements pointed to by codeIn, codeIn[message[i]%100] is suspicious because there's a potential for message[i]%100 to be an invalid index. While we're on this note, it might be wise to make message[i] an unsigned int so that it can't be negative. The format specifier (for printf and scanf) corresponding to unsigned int is %u.
while ( !feof(messageFile) ) is wrong because the EOF flag isn't set until an attempt is made at reading. Between attempting to read and your EOF test, however, you've incremented counter which means you've counted one too many items. Perhaps your loop should look like this:
while (fscanf(messageFile, "%d", (message+counter)) == 1)
{
counter++;
}
Note that this code assumes you've chosen to keep message[i] as an int. If you've chosen to use unsigned int instead, of course you'll want to use the %u format specifier.
You can probably see that feof is mostly superfluous... You can usually test for erroneous reads by checking the return value. Try to avoid feof in the future.
Your main function has a return type of int, yet at the end of it you have a return; statement which doesn't return an int value. Remove that. It's probably causing errors during compilation.
Presumably, when argv != 3 you want to return from main so you don't end up processing invalid arguments... Make sure you return an int value, e.g.
if (argc != 3) {
printf("This program takes two arguments: the name of the file with the code, and the name of the file with the encoded message\n");
return 0;
}
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!