Related
I'm trying to create a program that receives a word and two chars to be replaced inside that word using pointers, but I cannot figure it out how.
my .h file:
typedef struct pointer{
char message[100];
char start;
char end;
} tPointer;
tPointer readMsg();
my .c file:
tPointer readMsg(){
tPointer nPointer;
printf("Input word, char start and char end (between spaces): ");
scanf("%s %c %c", nPointer.message, &nPointer.start, &nPointer.end);
return(nPointer);
}
my main.c file:
int main(){
tPointer pointer;
pointer = readMsg();
printf("%s, %c, %c\n", pointer.message, pointer.start, pointer.end);
for (int i = 0; pointer.message[i] != '\0'; ++i) {
if (pointer.start == pointer.message[i])
{
printf("tem: %c\n", pointer.message[i]);
}
}
return 0;
}
The start input is the character inside message that I want to change, the end input is the character I will trade with start to create the new message
I have to swap the characters inside that for loop, right?
Example1:
message = stack
start = a
end = b
new message = stbck
Example2:
message = overflow
start = o
end = z
new message = zverflzw
From your updated examples, it looks like you want to find the first occurrence of start and replace that character with end. Much of the confusion with your question stems from the fact that start and end have nothing to do with what it is you are attempting to do. Use descriptive names to help keep your logic straight, e.g.
#include <stdio.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
typedef struct {
char message[MAXC]; /* don't use magic-numbers, use a constant */
char find; /* use descriptive names */
char replace;
} tPointer;
(note: the struct-tag pointer was unused and omitted)
With your tPointer readMsg() function, you have no way to tell if the input succeeded or if the string and two characters requested were actually entered. It is critical that you validate EVERY input and validate the input matches what was requested.
Rather than returning type tPointer (which if uninitialized provides no way to tell what was entered), pass a tPointer pointer as a parameter and return type int with 0 on failure and 1 on success (or vice-versa -- up to you). That way you can check the return in the caller and know whether readMsg() succeeded or failed, e.g.
/* choose return type that can indicate success/failure
* of input operation. Returns 1 on success, 0 otherwise
*/
int readMsg (tPointer *tp)
{
char buf[MAXC]; /* buffer to hold line of input */
/* read input into buf */
fputs ("Input word, find char, replace char (between spaces): ", stdout);
if (!fgets (buf, MAXC, stdin)) { /* always validate EVERY input */
puts ("(user canceled input)");
return 0;
}
/* separate using sscanf() -- always validate correct input */
if (sscanf (buf, "%s %c %c", tp->message, &tp->find, &tp->replace) != 3) {
fputs ("error: invalid input.\n", stderr);
return 0;
}
return 1; /* return success */
}
(note: the user generating a manual EOF to cancel input by pressing Ctrl + d (or Ctrl + z on windows) is valid input by the user that should be handled)
Also note, that a larger buffer was used to read the entire line into buf and then separate the string and characters in buf using sscanf(). This insures that if any EXTRA characters are entered by the user they are read and discarded. Failure to do so would leave any extra characters in stdin (unread) just waiting to bite you on your next attempted read (such as if you were doing the read in a loop to let the user enter more than one set of data)
Since you wrote a function for readMsg(), you may as well write another for replaceChar(). The return type can be int again and you can pass your tPointer pointer as a parameter, returning 1 on success or 0 if the find character was not found in message, e.g.
/* replace 'find' char with 'replace' char.
* returns 1 on success, 0 if find char not found.
*/
int replaceChar (tPointer *tp)
{
for (int i = 0; tp->message[i]; i++) { /* loop over each char */
if (tp->message[i] == tp->find) { /* check if find char */
tp->message[i] = tp->replace; /* replace it */
return 1; /* return success */
}
}
return 0; /* return failure - char not found */
}
Now in main() check the return of each function to ensure both succeed before outputting the results. On failure, handle the error. You can do something similar to:
int main (void) {
tPointer ptr = { .message = "" }; /* initialize struct all zero */
if (!readMsg (&ptr)) { /* validate read */
return 1; /* handle failure */
}
if (!replaceChar (&ptr)) { /* validate replacement */
fprintf (stderr, "error: char not found '%c'.\n", ptr.find);
return 1; /* handle failure */
}
/* output results */
printf ("find = %c\nreplace = %c\nupdated string: %s\n",
ptr.find, ptr.replace, ptr.message);
}
(note: you can paste the 4-pieces of code together for the complete program)
Example Use/Output
A valid replacement:
$ ./bin/tpointer-find-replace
Input word, find char, replace char (between spaces): Overflow o E
find = o
replace = E
updated string: OverflEw
Invalid input:
$ ./bin/tpointer-find-replace
Input word, find char, replace char (between spaces): Overflow o
error: invalid input.
Character Not Found:
$ ./bin/tpointer-find-replace
Input word, find char, replace char (between spaces): Overflow s y
error: char not found 's'.
User cancels input by generating manual EOF:
$ ./bin/tpointer-find-replace
Input word, find char, replace char (between spaces): (user canceled input)
Let me know if you have further questions.
I'm having some troubles using strtok function.
As an exercise I have to deal with a text file by ruling out white spaces, transforming initials into capital letters and printing no more than 20 characters in a line.
Here is a fragment of my code:
fgets(sentence, SIZE, f1_ptr);
char *tok_ptr = strtok(sentence, " \n"); //tokenazing each line read
tok_ptr[0] = toupper(tok_ptr[0]); //initials to capital letters
int num = 0, i;
while (!feof(f1_ptr)) {
while (tok_ptr != NULL) {
for (i = num; i < strlen(tok_ptr) + num; i++) {
if (i % 20 == 0 && i != 0) //maximum of 20 char per line
fputc('\n', stdout);
fputc(tok_ptr[i - num], stdout);
}
num = i;
tok_ptr = strtok(NULL, " \n");
if (tok_ptr != NULL)
tok_ptr[0] = toupper(tok_ptr[0]);
}
fgets(sentence, SIZE + 1, f1_ptr);
tok_ptr = strtok(sentence, " \n");
if (tok_ptr != NULL)
tok_ptr[0] = toupper(tok_ptr[0]);
}
The text is just a bunch of lines I just show as a reference:
Watch your thoughts ; they become words .
Watch your words ; they become actions .
Watch your actions ; they become habits .
Watch your habits ; they become character .
Watch your character ; it becomes your destiny .
Here is what I obtain in the end:
WatchYourThoughts;Th
eyBecomeWords.WatchY
ourWords;THeyBecomeA
ctions.WatchYourActi
ons;TheyBecomeHabits
.WatchYourHabits;The
yBecomeCharacteR.Wat
chYourCharacter;ItBe
comesYourDEstiny.Lao
-Tze
The final result is mostly correct, but sometimes (for example "they" in they become (and only in that case) or "destiny") words are not correctly tokenized. So for example "they" is split into "t" and "hey" resulting in THey (DEstiny in the other instance) after the manipulations I made.
Is it some bug or am I missing something? Probably my code is not that efficient and some condition may end up being critical...
Thank you for the help, it's not that big of a deal, I just don't understand why such a behaviour is occurring.
You have a large number of errors in your code and you are over-complicating the problem. The most pressing error is Why is while ( !feof (file) ) always wrong? Why? Trace the execution-path within your loop. You attempt to read with fgets(), and then you use sentence without knowing whether EOF was reached calling tok_ptr = strtok(sentence, " \n"); before you ever get around to checking feof(f1_ptr)
What happens when you actually reach EOF? That IS "Why while ( !feof (file) ) is always wrong?" Instead, you always want to control your read-loop with the return of the read function you are using, e.g. while (fgets(sentence, SIZE, f1_ptr) != NULL)
What is it you actually need your code to do?
The larger question is why are you over-complicating the problem with strtok, and arrays (and fgets() for that matter)? Think about what you need to do:
read each character in the file,
if it is whitespace, ignore it, set the in-word flag false,
if a non-whitespace, if 1st char in word, capitalize it, output the char, set the in-word flag true and increment the number of chars output to the current line, and finally
if it is the 20th character output, output a newline and reset the counter zero.
The bare-minimum tools you need from your C-toolbox are fgetc(), isspace() and toupper() from ctype.h, a counter for the number of characters output, and a flag to know if the character is the first non-whitespace character after a whitespace.
Implementing the logic
That makes the problem very simple. Read a character, is it whitespace?, set your in-word flag false, otherwise if your in-word flag is false, capitalize it, output the character, set your in-word flag true, increment your word count. Last thing you need to do is check if your character-count has reached the limit, if so output a '\n' and reset your character-count zero. Repeat until you run out of characters.
You can turn that into a code with something similar to the following:
#include <stdio.h>
#include <ctype.h>
#define CPL 20 /* chars per-line, if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
int c, in = 0, n = 0; /* char, in-word flag, no. of chars output in line */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while ((c = fgetc(fp)) != EOF) { /* read / validate each char in file */
if (isspace(c)) /* char is whitespace? */
in = 0; /* set in-word flag false */
else { /* otherwise, not whitespace */
putchar (in ? c : toupper(c)); /* output char, capitalize 1st in word */
in = 1; /* set in-word flag true */
n++; /* increment character count */
}
if (n == CPL) { /* CPL limit reached? */
putchar ('\n'); /* output newline */
n = 0; /* reset cpl counter */
}
}
putchar ('\n'); /* tidy up with newline */
if (fp != stdin) /* close file if not stdin */
fclose (fp);
}
Example Use/Output
Given your input file stored on my computer in dat/text220.txt, you can produce the output you are looking for with:
$ ./bin/text220 dat/text220.txt
WatchYourThoughts;Th
eyBecomeWords.WatchY
ourWords;TheyBecomeA
ctions.WatchYourActi
ons;TheyBecomeHabits
.WatchYourHabits;The
yBecomeCharacter.Wat
chYourCharacter;ItBe
comesYourDestiny.
(the executable for the code was compiled to bin/text220, I usually keep separate dat, obj, and bin directories for data, object files and executables to keep by source code directory clean)
note: by reading from stdin by default if no filename is provided as the first argument to the program, you can use your program to read input directly, e.g.
$ echo "my dog has fleas - bummer!" | ./bin/text220
MyDogHasFleas-Bummer
!
No fancy string functions required, just a loop, a character, a flag and a counter -- the rest is just arithmetic. It's always worth trying to boils your programming problems down to basic steps and then look around your C-toolbox and find the right tool for each basic step.
Using strtok
Don't get me wrong, there is nothing wrong with using strtok and it makes a fairly simple solution in this case -- the point I was making is that for simple character-oriented string-processing, it's often just a simple to loop over the characters in the line. You don't gain any efficiencies using fgets() with an array and strtok(), the read from the file is already placed into a buffer of BUFSIZ1.
If you did want to use strtok(), you should control you read-loop your with the return from fgets()and then you can tokenize with strtok() also checking its return at each point. A read-loop with fgets() and a tokenization loop with strtok(). Then you handle first-character capitalization and then limiting your output to 20-chars per-line.
You could do something like the following:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define CPL 20 /* chars per-line, if you need a constant, #define one (or more) */
#define MAXC 1024
#define DELIM " \t\r\n"
void putcharCPL (int c, int *n)
{
if (*n == CPL) { /* if n == limit */
putchar ('\n'); /* output '\n' */
*n = 0; /* reset value at mem address 0 */
}
putchar (c); /* output character */
(*n)++; /* increment value at mem address */
}
int main (int argc, char **argv) {
char line[MAXC]; /* buffer to hold each line */
int n = 0; /* no. of chars ouput in line */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fgets (line, MAXC, fp)) /* read each line and tokenize line */
for (char *tok = strtok (line, DELIM); tok; tok = strtok (NULL, DELIM)) {
putcharCPL (toupper(*tok), &n); /* convert 1st char to upper */
for (int i = 1; tok[i]; i++) /* output rest unchanged */
putcharCPL (tok[i], &n);
}
putchar ('\n'); /* tidy up with newline */
if (fp != stdin) /* close file if not stdin */
fclose (fp);
}
(same output)
The putcharCPL() function is just a helper that checks if 20 characters have been output and if so outputs a '\n' and resets the counter. It then outputs the current character and increments the counter by one. A pointer to the counter is passed so it can be updated within the function making the updated value available back in main().
Look things over and let me know if you have further questions.
footnotes:
1. Depending on your version of gcc, the constant in the source setting the read-buffer size may be _IO_BUFSIZ. _IO_BUFSIZ was changed to BUFSIZ here: glibc commit 9964a14579e5eef9 For Linux BUFSIZE is defined as 8192 (512 on Windows).
This is actually a much more interesting OP from a professional point of view than some of the comments may suggest, despite the 'newcomer' aspect of the question, which may sometimes raise fairly deep, underestimated issues.
The fun thing is that on my platform (W10, MSYS2, gcc v.10.2), your code runs fine with correct results:
WatchYourThoughts;Th
eyBecomeWords.WatchY
ourWords;TheyBecomeA
ctions.WatchYourActi
ons;TheyBecomeHabits
.WatchYourHabits;The
yBecomeCharacter.Wat
chYourCharacter;ItBe
comesYourDestiny.
So first, congratulations, newcomer: your coding is not that bad.
This points to how different compilers may or may not protect against limited inappropriate coding or specification misuse, may or may not protect stacks or heaps.
This said, the comment by #Andrew Henle pointing to an illuminating answer about feof is quite relevant.
If you follow it and retrieve your feof test, just moving it down after read checks, not before (as below). Your code should yield better results (note: I will just alter your code minimally, deliberately ignoring lesser issues):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#define SIZE 100 // add some leeway to avoid off-by-one issues
int main()
{
FILE* f1_ptr = fopen("C:\\Users\\Public\\Dev\\test_strtok", "r");
if (! f1_ptr)
{
perror("Open issue");
exit(EXIT_FAILURE);
}
char sentence[SIZE] = {0};
if (NULL == fgets(sentence, SIZE, f1_ptr))
{
perror("fgets issue"); // implementation-dependent
exit(EXIT_FAILURE);
}
errno = 0;
char *tok_ptr = strtok(sentence, " \n"); //tokenizing each line read
if (tok_ptr == NULL || errno)
{
perror("first strtok parse issue");
exit(EXIT_FAILURE);
}
tok_ptr[0] = toupper(tok_ptr[0]); //initials to capital letters
int num = 0;
size_t i = 0;
while (1) {
while (1) {
for (i = num; i < strlen(tok_ptr) + num; i++) {
if (i % 20 == 0 && i != 0) //maximum of 20 char per line
fputc('\n', stdout);
fputc(tok_ptr[i - num], stdout);
}
num = i;
tok_ptr = strtok(NULL, " \n");
if (tok_ptr == NULL) break;
tok_ptr[0] = toupper(tok_ptr[0]);
}
if (NULL == fgets(sentence, SIZE, f1_ptr)) // let's get away whith annoying +1,
// we have enough headroom
{
if (feof(f1_ptr))
{
fprintf(stderr, "\n%s\n", "Found EOF");
break;
}
else
{
perror("Unexpected fgets issue in loop"); // implementation-dependent
exit(EXIT_FAILURE);
}
}
errno = 0;
tok_ptr = strtok(sentence, " \n");
if (tok_ptr == NULL)
{
if (errno)
{
perror("strtok issue in loop");
exit(EXIT_FAILURE);
}
break;
}
tok_ptr[0] = toupper(tok_ptr[0]);
}
return 0;
}
$ ./test
WatchYourThoughts;Th
eyBecomeWords.WatchY
ourWords;TheyBecomeA
ctions.WatchYourActi
ons;TheyBecomeHabits
.WatchYourHabits;The
yBecomeCharacter.Wat
chYourCharacter;ItBe
comesYourDestiny.
Found EOF
This is my csv file, i want to get only those row which start with character "A" so i got my output but with some addition column as '0' please help me to find were i went wrong?
And one more thing i want to remove specific column like bread,anName,ot
Name,id,bread,anName,Ot,number
A,1,animal,tiger,op,8.1
M,2,animal,toper,ip,9.1
A1,7,animal,dog,cp,Na11
A2,9,animal,mouse,ap,0
A23,9,animal,pouch,gp,Na11
#include <stdio.h>
#include <stdlib.h>
#define NUMLETTERS 100
typedef struct {
char Name[100];
int id;
char number[100];
} record_t;
int main(void) {
FILE *fp;
record_t records[NUMLETTERS];
int count = 0, i;
fp = fopen("letter.csv", "r");
if (fp == NULL) {
fprintf(stderr, "Error reading file\n");
return 1;
}
while (fscanf(fp, "%s,%d,%s", records[count].name, &records[count].id, records[count].number) == 1)
count++;
for (i = 0; i < count; i++) {
if(records[i].Name[0] == 'A'){
printf("%s,%d,%s\n", records[i].Name, records[i].id, records[i].number);
}
}
fclose(fp);
return 0;
}
i want output as:
A,1,8.1
A1,7,Na11
A2,9,0
A23,9,Na11
You have two problems:
The %s format specifier tells fscanf to read a space-delimited string. Since the the records aren't space-delimited the first %s will read the whole line.
The fscanf function returns the number of successfully parsed elements it handled. Since you attempt to read three values you should compare with 3 instead of 1.
Now for one way how to solve the first problem: Use the %[ format specifier. It can handle simple patterns and, most importantly, negative patterns (read while input does not match).
So you could tell fscanf to read a string until it finds a comma by using %[^,]:
fscanf(fp, " %[^,],%d,%s", records[count].Refdes, &records[count].pin, records[count].NetName)
The use of the %[ specifier is only needed for the first string, as the second will be space-delimited (the newline).
Also note that there's a space before the %[ format, to read and ignore leading white-space, like for example the newline from the previous line.
i want to get only those row which start with character "A"
i want to remove the number which coming between A and tiger,
If I understand you correctly and you only want to store rows beginning with 'A', then I would adjust your approach to read each line with fgets() and then check whether the first character in the buffer is 'A', if so, continue; and get the next line. The for those lines that do start with 'A', simply use sscanf to parse the data into your array of struct records.
For your second part of removing the number between 'A' and "tiger", there is a difference between what you store and what you output (this comes into play in storing only records beginning with 'A' as well), but for those structs stored where the line starts with 'A', you can simply not-output the pin struct member to get the output you want.
The approach to reading a line at a time will simply require that you declare an additional character array (buffer), called buf below, to read each line into with fgets(), e.g.
char buf[3 * NUMLETTERS] = "";
...
/* read each line into buf until a max of NUMLETTERS struct filled */
while (count < NUMLETTERS && fgets (buf, sizeof buf, fp)) {
record_t tmp = { .Refdes = "" }; /* temporary struct to read into */
if (*buf != 'A') /* if doesn't start with A get next */
continue;
/* separate lines beginning with 'A' into struct members */
if (sscanf (buf, " %99[^,],%d,%99[^\n]",
tmp.Refdes, &tmp.pin, tmp.NetName) == 3)
records[count++] = tmp; /* assign tmp, increment count */
else
fprintf (stderr, "%d A record - invalid format.\n", count + 1);
}
A short example putting that to use and (since we are not sure what "remove" is intended to be), we have included a pre-processor conditional that will only output the .Refdes and .NetName members by default, but if you either #define WITHPIN or include the define in your compile string (e.g. -DWITHPIN) it will output the .pin member as well.
#include <stdio.h>
#include <stdlib.h>
#define NUMLETTERS 100
typedef struct {
char Refdes[NUMLETTERS];
int pin;
char NetName[NUMLETTERS];
} record_t;
int main (int argc, char **argv) {
record_t records[NUMLETTERS];
char buf[3 * NUMLETTERS] = "";
int count = 0, i;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
/* read each line into buf until a max of NUMLETTERS struct filled */
while (count < NUMLETTERS && fgets (buf, sizeof buf, fp)) {
record_t tmp = { .Refdes = "" }; /* temporary struct to read into */
if (*buf != 'A') /* if doesn't start with A get next */
continue;
/* separate lines beginning with 'A' into struct members */
if (sscanf (buf, " %99[^,],%d,%99[^\n]",
tmp.Refdes, &tmp.pin, tmp.NetName) == 3)
records[count++] = tmp; /* assign tmp, increment count */
else
fprintf (stderr, "%d A record - invalid format.\n", count + 1);
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
for (i = 0; i < count; i++)
#ifdef WITHPIN
printf ("%-8s %2d %s\n",
records[i].Refdes, records[i].pin, records[i].NetName);
#else
printf ("%-8s %s\n", records[i].Refdes, records[i].NetName);
#endif
}
Example Use/Output
$ ./bin/getaonly dat/getasonly.txt
A tiger
A1 dog
A2 mouse
A23 pouch
If you define -DWITHPIN in your compile string, then you will get all three outputs:
$ ./bin/getaonly dat/getasonly.txt
A 1 tiger
A1 7 dog
A2 9 mouse
A23 9 pouch
(note: with the data stored in your array, you can adjust the output format to anything you need)
Since there is some uncertainty whether you want to store all and output only records beginning with 'A' or only want to store records beginning with 'A' -- let me know if I need to make changes and I'm happy to help further.
I have a file with a series of words separated by a white space. For example file.txt contains this: "this is the file". How can I use fscanf to take word by word and put each word in an array of strings?
Then I did this but I don't know if it's correct:
char *words[100];
int i=0;
while(!feof(file)){
fscanf(file, "%s", words[i]);
i++;
fscanf(file, " ");
}
When reading repeated input, you control the input loop with the input function itself (fscanf in your case). While you can also loop continually (e.g. for (;;) { ... }) and check independently whether the return is EOF, whether a matching failure occurred, or whether the return matches the number of conversion specifiers (success), in your case simply checking that the return matches the single "%s" conversion specifier is fine (e.g. that the return is 1).
Storing each word in an array, you have several options. The most simple is using a 2D array of char with automatic storage. Since the longest non-medical word in the Unabridged Dictionary is 29-characters (requiring a total of 30-characters with the nul-terminating character), a 2D array with a fixed number of rows and fixed number of columns of at least 30 is fine. (dynamically allocating allows you to read and allocate memory for as many words as may be required -- but that is left for later.)
So to set up storage for 128 words, you could do something similar to the following:
#include <stdio.h>
#define MAXW 32 /* if you need a constant, #define one (or more) */
#define MAXA 128
int main (int argc, char **argv) {
char array[MAXA][MAXW] = {{""}}; /* array to store up to 128 words */
size_t n = 0; /* word index */
Now simply open your filename provided as the first argument to the program (or read from stdin by default if no argument is given), and then validate that your file is open for reading, e.g.
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
Now to the crux of your read-loop. Simply loop checking the return of fscanf to determine success/failure of the read, adding words to your array and incrementing your index on each successful read. You must also include in your loop-control a check of your index against your array bounds to ensure you do not attempt to write more words to your array than it can hold, e.g.
while (n < MAXA && fscanf (fp, "%s", array[n]) == 1)
n++;
That's it, now just close the file and use your words stored in your array as needed. For example just printing the stored words you could do:
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (size_t i = 0; i < n; i++)
printf ("array[%3zu] : %s\n", i, array[i]);
return 0;
}
Now just compile it, With Warnings Enabled (e.g. -Wall -Wextra -pedantic for gcc/clang, or /W3 on (VS, cl.exe) and then test on your file. The full code is:
#include <stdio.h>
#define MAXW 32 /* if you need a constant, #define one (or more) */
#define MAXA 128
int main (int argc, char **argv) {
char array[MAXA][MAXW] = {{""}}; /* array to store up to 128 words */
size_t n = 0; /* word index */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (n < MAXA && fscanf (fp, "%s", array[n]) == 1)
n++;
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (size_t i = 0; i < n; i++)
printf ("array[%3zu] : %s\n", i, array[i]);
return 0;
}
Example Input File
$ cat dat/thefile.txt
this is the file
Example Use/Output
$ ./bin/fscanfsimple dat/thefile.txt
array[ 0] : this
array[ 1] : is
array[ 2] : the
array[ 3] : file
Look things over and let me know if you have further questions.
strtok() might be a function that can help you here.
If you know that the words will be separated by whitespace, then calling strtok will return the char pointer to the start of the next word.
Sample code from https://www.systutorials.com/docs/linux/man/3p-strtok/
#include <string.h>
...
char *token;
char *line = "LINE TO BE SEPARATED";
char *search = " ";
/* Token will point to "LINE". */
token = strtok(line, search);
/* Token will point to "TO". */
token = strtok(NULL, search);
In your case, the space character would also act as a delimiter in the line.
Note that strtok might modify the string passed in, so if you need to you should make a deep copy using something like malloc.
It might also be easier to use fread() to read a block from a file
As mentioned in comments, using feof() does not work as would be expected. And, as described in this answer unless the content of the file is formatted with very predictable content, using any of the scanf family to parse out the words is overly complicated. I do not recommend using it for that purpose.
There are many other, better ways to read content of a file, word by word. My preference is to read each line into a buffer, then parse the buffer to extract the words. This requires determining those characters that may be in the file, but would not be considered part of a word. Characters such as \n,\t, (space), -, etc. should be considered delimiters, and can be used to extract the words. The following is a recipe for extracting words from a file: (example code for a few of the items is included below these steps.)
Read file to count words, and get the length of the longest word.
Use count, and longest values from 1st step to allocate memory for words.
Rewind the file.
Read file line by line into a line buffer using while(fgets(line, size, fp))
Parse each new line into words using delimiters and store each word into arrays of step 2.
Use resulting array of words as necessary.
free all memory allocated when finished with arrays
Some example of code to do some of these tasks:
// Get count of words, and longest word in file
int longestWord(char *file, int *nWords)
{
FILE *fp=0;
int cnt=0, longest=0, numWords=0;
int c;
fp = fopen(file, "r");
if(fp)
{
// if((strlen(buf) > 0) && (buf[0] != '\t') && (buf[0] != '\n') && (buf[0] != '\0')&& (buf[0] > 0))
while ( (c = fgetc(fp) ) != EOF )
{
if ( isalnum (c) ) cnt++;
else if ( ( ispunct (c) ) || ( isspace(c) ) || (c == '\0' ))
{
(cnt > longest) ? (longest = cnt, cnt=0) : (cnt=0);
numWords++;
}
}
*nWords = numWords;
fclose(fp);
}
else return -1;
return longest;
}
// Create indexable memory for word arrays
char ** Create2DStr(ssize_t numStrings, ssize_t maxStrLen)
{
int i;
char **a = {0};
a = calloc(numStrings, sizeof(char *));
for(i=0;i<numStrings; i++)
{
a[i] = calloc(maxStrLen + 1, 1);
}
return a;
}
Usage: For a file with 25 words, the longest being 80 bytes:
char **strArray = Create2DStr(25, 80+1);//creates 25 array locations
//each 80+1 characters long
//(+1 is room for null terminator.)
int i=0;
char words[50][50];
while(fscanf(file, " %s ", words[i]) != EOF)
i++;
I wouldn't entirely recommend doing it this way, because of the unknown amount of words in the file, and the unknown length of a "word". Either can be over the size of '50'. Just do it dynamically, instead. Still, this should show you how it works.
How can I use fscanf to take word by word and put each word in an array of strings?
Read each word twice: first to find length via "%n". 2nd time, save it. (Inefficient yet simple)
Re-size strings as you go. Again inefficient, yet simple.
// Rough untested sample code - still need to add error checking.
size_t string_count = 0;
char **strings = NULL;
for (;;) {
long pos = ftell(file);
int n = 0;
fscanf(file, "%*s%n", &n); // record where scanning a "word" stopped
if (n == 0) break;
fseek(file, pos, SEEK_SET); // go back;
strings = realloc(strings, sizeof *strings * (string_count+1));// increase array size
strings[string_count] = malloc(n + 1u); // Get enough memory for the word
fscanf(file, "%s ", strings[string_count] ); // read/save word
}
// use strings[], string_count
// When done, free each strings[] and then strings
I have a problem which i cant fix i need to check last three words of frist sentence with last three words of fourth sentence
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
char firstRow[256];
char secondRow[256];
char thirdRow[256];
char fourthRow[256];
printf("Enter four row of lyrcis:\n");
gets(firstRow);
gets(secondRow);
gets(thirdRow);
gets(fourthRow);
if ( strcmp(a1+strlen(a1)-1, a4+strlen(a4)-1) &&
strcmp(a1+strlen(a1)-2, a4+strlen(a4)-2) &&
strcmp(a1+strlen(a1)-3, a4+strlen(a4)-3) == 0 ){
printf("Good job last three words of first and fourth sentence are same");
}
else {
printf("nothing");
}
return 0;
}
This is something i tried but obviously problem is that i cant use if like that with only one strcmp it works. Maybe i need strcpy command? Help!
First -- Do not use 'gets'. It is horribly insecure. There is no limitation on the number of characters it will read or whether the size of the buffer you provide has adequate storage. That allows for buffer overrun exploits and is the primary reason it has been dropped from the C library. If your professor insists on using it -- find a new professor.
The other problem you have is failing to validate each step in your process. You fail to check if gets actually read anything before passing the pointers to strcmp or strlen.
Further, your indexing is nonsense. strlen(x) - n doesn't index the end - n word in the buffer. For that you have to tokenize the string (split it into words). There are a number of ways to do it.
One method that works no matter what is simply finding the end of the string (e.g. strlen(line) - 1) and using a pointer to iterate from the end of the string towards the start until your first whitespace is found (or you reach the beginning).
The C library (in string.h) provides strrchr which automates that process for you. It will start at the end of a string and iterate backwards until it finds the first occurrence of the character you tell it to find returning a pointer to that character (or returning NULL it that character is not found). The only downside here is you are limited to search for a single character.
The C library (in string.h) provides strtok, which does not provide for a reverse search, but does provide the ability to split a string based on a set of delimiters you provide (e.g. it could handle splitting on any one of space, tab, '.', etc..). Here you simply store the pointers to each of the words (or a copy of the words) and take the last 3 indexes for comparison.
The following provides an example that uses strrchr presuming your words or separated by one (or more) spaces. Both the method used below and strtok modify the original string, so make a copy of a string before parsing if the string is originally stored in read-only memory (e.g. a string literal).
The program expects the filename to read to be provided as the first argument (or it will read from stdin if no argument is provided). Additional comments are provided in the code, in-line, below:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef BUF_SIZ /* if you need constants... define them */
#define BUF_SIZ 8192 /* don't put 'magic' numbers in code */
#endif
#define NLAST 3
int main (int argc, char **argv) {
size_t len = 0;
char line1[BUF_SIZ] = "",
line4[BUF_SIZ] = "",
*last[NLAST] = { NULL };
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;
}
if (fgets (line1, BUF_SIZ, fp) == NULL) { /* read 1st line */
fprintf (stderr, "error: failed to read line1.\n");
return 1;
}
for (int i = 0; i < NLAST; i++) /* read/discard lines 2,3 read line 4 */
if (fgets (line4, BUF_SIZ, fp) == NULL) {
fprintf (stderr, "error: failed to read line4.\n");
return 1;
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
len = strlen (line1); /* get length of line1 */
if (len && line1[len-1] == '\n') /* validate last is '\n' */
line1[--len] = 0; /* overwrite with nul-character */
else { /* error: handle line too long or no POSIX EOL */
fprintf (stderr, "error: line1 too long or no POSIX EOL.\n");
return 1;
}
len = strlen (line4); /* same thing for line4 */
if (len && line4[len-1] == '\n')
line4[--len] = 0;
else {
fprintf (stderr, "error: line4 too long or no POSIX EOL.\n");
return 1;
}
if (!*line1 || !*line4) { /* test if either is empty-string */
fprintf (stderr, "error: one or both line(s) empty.\n");
return 1;
}
for (int i = 0; i < NLAST; i++) { /* loop NLAST times */
char *p1 = strrchr (line1, ' '), /* get pointer to last ' ' */
*p4 = strrchr (line4, ' ');
if (!p1) { /* validate result of strrchr */
if (i < NLAST - 1) { /* if not last iteration - handle error */
fprintf (stderr, "error: only '%d' words in line1.\n", i+1);
return 1;
}
else /* if last iteration, assign line to pointer */
p1 = line1;
}
if (!p4) { /* same for line4 */
if (i < NLAST - 1) {
fprintf (stderr, "error: only '%d' words in line4.\n", i+1);
return 1;
}
else
p4 = line1;
}
/* copy to last array in order - checking if p1 is beginning of line */
last[NLAST - 1 - i] = p1 == line1 ? p1 : p1 + 1;
while (p1 > line1 && *p1 == ' ') /* nul-terminate at space */
*p1-- = 0;
while (p4 > line4 && *p4 == ' ')
*p4-- = 0;
}
printf ("\nthe last %d words in lines 1 & 4 are the same:\n", NLAST);
for (int i = 0; i < NLAST; i++)
printf (" %s\n", last[i]);
return 0;
}
Example Input File
$ cat dat/last14-3.txt
My dog has fleas
My snake has none
The cats have none
Cats are lucky because the dog has fleas
Example Use/Output
$ ./bin/lines1_4_last3 < dat/last14-3.txt
the last 3 words in lines 1 & 4 are the same:
dog
has
fleas
Regardless which method you choose to tokenize the lines, you must validate each step along the way. Look things over and make sure you understand why each validation was necessary, if not, just ask and I'm happy to help further.