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.
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 .txt file that contains data in this format:
xxxx: 0.9467,
yyyy: 0.9489,
zzzz: 0.78973,
hhhh: 0.8874,
yyyy: 0.64351,
xxxx: 0.8743,
and so on...
Let's say that my C program receives, as input, the string yyyy. The program should, simply, return all the instances of yyyy in the .txt file and the average of all their numerical values.
int main() {
FILE *filePTR;
char fileRow[100000];
if (fopen_s(&filePTR, "file.txt", "r") == 0) {
while (fgets(fileRow, sizeof fileRow, filePTR) != NULL) {
if (strstr(fileRow, "yyyy") != NULL) { // Input parameter
printf("%s", fileRow);
}
}
fclose(filePTR);
printf("\nEnd of the file.\n");
} else {
printf("ERROR! Impossible to read the file.");
}
return 0;
}
This is my code right now. I don't know how to:
Isolate the numerical values
actually convert them to double type
average them
I read something about the strtok function (just to start), but I would need some help...
You have started off on the right track and should be commended for using fgets() to read a complete line from the file on each iteration, but your choice of strstr does not ensure the prefix you are looking for is found at the beginning of the line.
Further, you want to avoid hardcoding your search string as well as the file to open. main() takes arguments through argc and argv that let you pass information into your program on startup. See: C11 Standard - ยง5.1.2.2.1 Program startup(p1). Using the parameters eliminates your need to hardcode values by letting you pass the filename to open and the prefix to search for as arguments to your program. (which also eliminates the need to recompile your code simply to read from another filename or search for another string)
For example, instead of hardcoding values, you can use the parameters to main() to open any file and search for any prefix simply using something similar to:
#include <stdio.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
char buf[MAXC] = "", *str = NULL; /* buffer for line and ptr to search str */
size_t n = 0, len = 0; /* counter and search string length */
double sum = 0; /* sum of matching lines */
FILE *fp = NULL; /* file pointer */
if (argc < 3) { /* validate 2 arguments given - filename, search_string */
fprintf (stderr, "error: insufficient number of arguments\n"
"usage: %s filename search_string\n", argv[0]);
return 1;
}
if (!(fp = fopen (argv[1], "r"))) { /* open/validate file open for reading */
perror ("fopen-filename");
return 1;
}
str = argv[2]; /* set pointer to search string */
len = strlen (str); /* get length of search string */
...
At this point in your program, you have opened the file passed as the first argument and have validated that it is open for reading through the file-stream pointer fp. You have passed in the prefix to search for as the second argument, assigned it to the pointer str and have obtained the length of the prefix and have stored in in len.
Next you want to read each line from your file into buf, but instead of attempting to match the prefix with strstr(), you can use strncmp() with len to compare the beginning of the line read from your file. If the prefix is found, you can then use sscanf to parse the double value from the file and add it to sum and increment the number of values stored in n, e.g.
while (fgets (buf, MAXC, fp)) { /* read each line into buf */
if (strncmp (buf, str, len) == 0) { /* if prefix matches */
double tmp; /* temporary double for parse */
/* parse with scanf, discarding prefix with assignment suppression */
if (sscanf (buf, "%*1023[^:]: %lf", &tmp) == 1) {
sum += tmp; /* add value to sum */
n++; /* increment count of values */
}
}
}
(note: above the assignment suppression operator for sscanf(), '*' allows you to read and discard the prefix and ':' without having to store the prefix in a second string)
All that remains is checking if values are contained in sum by checking your count n and if so, output the average for the prefix. Or, if n == 0 the prefix was not found in the file, e.g.:
if (n) /* if values found, output average */
printf ("prefix '%s' avg: %.4f\n", str, sum / n);
else /* output not found */
printf ("prefix '%s' -- not found in file.\n", str);
}
That is basically all you need. With it, you can read from any file you like and search for any prefix simply passing the filename and prefix as the first two arguments to your program. The complete example would be:
#include <stdio.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
char buf[MAXC] = "", *str = NULL; /* buffer for line and ptr to search str */
size_t n = 0, len = 0; /* counter and search string length */
double sum = 0; /* sum of matching lines */
FILE *fp = NULL; /* file pointer */
if (argc < 3) { /* validate 2 arguments given - filename, search_string */
fprintf (stderr, "error: insufficient number of arguments\n"
"usage: %s filename search_string\n", argv[0]);
return 1;
}
if (!(fp = fopen (argv[1], "r"))) { /* open/validate file open for reading */
perror ("fopen-filename");
return 1;
}
str = argv[2]; /* set pointer to search string */
len = strlen (str); /* get length of search string */
while (fgets (buf, MAXC, fp)) { /* read each line into buf */
if (strncmp (buf, str, len) == 0) { /* if prefix matches */
double tmp; /* temporary double for parse */
/* parse with scanf, discarding prefix with assignment suppression */
if (sscanf (buf, "%*1023[^:]: %lf", &tmp) == 1) {
sum += tmp; /* add value to sum */
n++; /* increment count of values */
}
}
}
if (n) /* if values found, output average */
printf ("prefix '%s' avg: %.4f\n", str, sum / n);
else /* output not found */
printf ("prefix '%s' -- not found in file.\n", str);
}
Example Use/Output
Using your data file stored in dat/prefixdouble.txt, you can search for each prefix in the file and obtain the average, e.g.
$ ./bin/prefixaverage dat/prefixdouble.txt hhhh
prefix 'hhhh' avg: 0.8874
$ ./bin/prefixaverage dat/prefixdouble.txt xxxx
prefix 'xxxx' avg: 0.9105
$ ./bin/prefixaverage dat/prefixdouble.txt yyyy
prefix 'yyyy' avg: 0.7962
$ ./bin/prefixaverage dat/prefixdouble.txt zzzz
prefix 'zzzz' avg: 0.7897
$ ./bin/prefixaverage dat/prefixdouble.txt foo
prefix 'foo' -- not found in file.
Much easier than having to recompile each time you want to search for another prefix. Look things over and let me know if you have further questions.
What I would like to do is read the whole first line of the file but then after the first line only read the following lines until whitespace is hit. My end goal is to ask the user what line they want to edit by adding/subtracting time to said line.
Sample File
My test file
00:19.1 123456
00:35.4 testing whitespace end
Desired Output
1: My test file
2: 00:19.1
3: 00:35.4
Code:
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *fptr1, *fptr2;
char filechar[40];
char c[50];
int line_number = 1;
int replace_line, temp = 1;
printf("Please enter a file name: ");
scanf("%s", &filechar);
if ((fptr1 = fopen(filechar, "r")) == NULL)
{
printf("Error locating desired file");
exit(1);
}
c = getc(fptr1);
while (c != EOF)
{
//printf("%d: %c",line_number, c);
printf("%s",c);
c = getc(fptr1);
//line_number++;
}
return 0;
}
In C you have character oriented input functions (e.g. getchar, fgetc), you have formatted input functions (e.g. the scanf family) and then you have line oriented input functions. (e.g. fgets and POSIX getline). When you are reading lines of data, line oriented input functions are the proper tool for the job. (taking user input with scanf has many pitfalls that new (and even not so new) C programmers fall into)
All line oriented functions read and include the '\n' in the buffer they fill. You can, and should, remove the newline from the resulting buffer if it will be used later on in your code. A simple
size_t n = strlen (buf);
if (buf[n-1] == '\n')
buf[--n] = 0;
is all you need to overwrite the trailing '\n' with a nul-terminating character. If you are just printing the line immediately and not storing it for later use, then it's not worth removing the newline (just account for it in your output format string).
Putting those pieces together, you can read each line, handle the first by simply outputting it, and for each remaining line, parse the time (presumable some elapsed time) from the full string read by fgets with sscanf and format the output as you specify. E.g.
#include <stdio.h>
#define MAXC 64 /* define constants, don't use magic number in code */
int main (int argc, char **argv) {
char buf[MAXC] = ""; /* buffer to hold each line -- size as reqd */
int line = 1;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (buf, sizeof buf, fp)) { /* read each line in file */
char et[MAXC] = ""; /* buffer for holding time */
if (line == 1) /* if 1st line, just print */
printf ("%d : %s", line, buf); /* note: \n included by fgets */
else {
if (sscanf (buf, "%s", et) != 1) { /* parse up to first whitespace */
fprintf (stderr, "error: invalid conversion, line %d\n", line);
return 1;
}
printf ("%d : %s\n", line, et); /* output elapsed time only */
}
line++; /* increment line count */
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
note: you should protect against buffer overrun on parse by including a field-width specifier in the sscanf format string (e.g. sscanf (buf, "%63s", et), and that is one place that all you can do is include magic numbers in your code because there is no way to directly specify a variable width specifier for sscanf -- unless you creatively use sprintf to create the format string ahead of time -- but that's for another day..
Example Input File
$ cat dat/et.txt
My test file
00:19.1 123456
00:35.4 testing whitespace end
Example Use/Output
$ ./bin/et <dat/et.txt
1 : My test file
2 : 00:19.1
3 : 00:35.4
Look things over and let me know if you have any further questions.
(note: I take the filename as the first argument to the program, or read from stdin if no filename is given. C provides for command line arguments -- use them. It's fine to prompt for input if needed, otherwise, its far easier just to specify arguments on the command line :)
Please try if this C code can help you. It just reads the file line by line and replaces whitespace with the string termination character \0.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
char* replace_char(char* str, char find, char replace){
char *current_pos = strchr(str,find);
while (current_pos){
*current_pos = replace;
current_pos = strchr(current_pos,find);
}
return str;
}
int main(void)
{
FILE * fp;
char * line = NULL;
size_t len = 0;
ssize_t read;
fp = fopen("/home/developer/CLionProjects/untitled4/download.out", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
int count=0;
while ((read = getline(&line, &len, fp)) != -1) {
if (count==0) printf("%s", line);
else printf("%s\n", replace_char(line, ' ', '\0'));
count++;
}
fclose(fp);
if (line)
free(line);
exit(EXIT_SUCCESS);
}
File
My test file
00:19.1 123456
00:35.4 testing whitespace end
Output
My test file
00:19.1
00:35.4
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++;
}
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!