I have to compare 2 strings, one from a file, and one from a user input, here is the file:
Password
abcdefg
Star_wars
jedi
Weapon
Planet
long
nail
car
fast
cover
machine
My_little
Alone
Love
Ghast
The code for getting the string from the line is fine but the code for comparing the 2 strings does not give the right output
int main(void){
int loop, line;
char str[512];
char string[512];
FILE *fd = fopen("Student Passwords.txt", "r");
if (fd == NULL) {
printf("Failed to open file\n");
return -1;
}
printf("Enter the string: ");
scanf("%s",string);
printf("Enter the line number to read : ");
scanf("%d", &line);
for(loop = 0;loop<line;++loop){
fgets(str, sizeof(str), fd);
}
printf("\nLine %d: %s\n", line, str);
if(strcmp(string,str) == 0 ){
printf("Match");
}else{
printf("No Match");
}
fclose(fd);
getch();
return 0;
}
Perhaps the str resets but i don't know, perhaps some of the talented programmers here can see the problem.
Anyone know what is wrong with my string comparison?
Correct output:
Input: jedi, 4 Output: Match
edit: Both strings are the same, in the same case
edit: dreamlax fixed this.
fgets() does not discard any newline character after reading, so it will be part of str, which will cause the comparison to fail since string won't have a newline character. To get around this, you simply need to remove the newline character from str.
str[strlen(str) - 1] = '\0';
if (strcmp(string, str) == 0)
// ...
Ideally, make sure strlen(str) > 0 first, otherwise you will invoke undefined behaviour.
Related
I'm a beginner programmer trying to learn C. Currently I'm taking a class and had a project assigned which I managed to finish pretty quickly, at least the main part of it. I had some trouble coding around the main() if functions though, because I started using some new functions (that is, fgets and strncmp). Now, my code works in my compiler, but not in any of the online compilers. So I'm wondering if I did something wrong with it, or if there is any way I can improve it.
Any help or contribution is appreciated, thanks!
Below is the code, the encrypt and decrypt functions are the first two functions before the main, where I believe most of the messy shortcut-code might be.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * Encrypt(char sentence[])
{
int primes[12] = {1,2,3,5,7,11,13,17,19,23,29,31};
int x = 0;
int counter = 0;
int ispositive = 1;
while(sentence[x] != 0)
{
if (counter == 0)
{
ispositive = 1;
}
else if(counter == 11)
{
ispositive = 0;
}
if (ispositive == 1)
{
sentence[x] = sentence[x] + primes[counter];
counter++;
}
else if (ispositive == 0)
{
sentence[x] = sentence[x] + primes[counter];
counter--;
}
x++;
}
return sentence;
}
char * Decrypt(char sentence[])
{
int primes[12] = {1,2,3,5,7,11,13,17,19,23,29,31};
int x = 0;
int counter = 0;
int ispositive = 1;
while(sentence[x] != 0)
{
if (counter == 0)
{
ispositive = 1;
}
else if(counter == 11)
{
ispositive = 0;
}
if (ispositive == 1)
{
sentence[x] = sentence[x] - primes[counter];
counter++;
}
else if (ispositive == 0)
{
sentence[x] = sentence[x] - primes[counter];
counter--;
}
x++;
}
return sentence;
}
int main()
{
char message[100];
char input[7];
char *p;
int c;
int condition = 1;
while(condition == 1)
{
printf("Would you like to Encrypt or Decrypt a message? (Type TurnOff to end the program) \n \n");
fgets(input,7, stdin);
fflush(stdin);
if (!strncmp(input,"Encrypt",strlen(input)))
{
printf("\n \n Enter the message you want to Encrypt below: \n \n");
fgets(message, 100, stdin);
Encrypt(message);
printf("\n Your encrypted message is: ");
printf("%s", message);
fflush(stdin);
printf("\n \n");
}
else if (!strncmp(input,"Decrypt",strlen(input)))
{
printf("\n \n Enter the message you want to Decrypt below: \n \n");
fgets(message, 100, stdin);
Decrypt(message);
printf("\n Your Decrypted message is: ");
printf("%s", message);
fflush(stdin);
printf("\n \n");
}
else if (!strncmp(input,"TurnOff",strlen(input)))
{
printf("\n \n Thank you for using the program! \n \n");
condition = 0;
}
else
{
printf("That's not a valid input \n \n");
}
}
}
After the printf you doing fflush(stdin) instead of you have to do fflush(stdout). Because you are printing the output. The output is printed in stdout. So, you have to flush the stdout buffer not stdin buffer.
You can use the strcmp instead of strncmp. Because in here you are comparing the hole character in the input array. So, the strcmp is enough.
strcmp(input, "Encrypt").
The strcmp or strncmp function get the input in array upto a null or the size of the string you are declared.
The size for the input array is too few.
lets take the input is like below.
Encrypt\n
sureshkumar\n
In here you first fgets in main function reads the upto "Encrypt" it does not skip the '\n'.
The '\n' is readed form another fgets. So, it does not get the encrypt message "sureshkumar".
So, you have to modify you code. You will increase the size for the input array.
And check the condition like below.
if(strcmp(input, "Encrypt\n") == 0)
{
/*
You will do what you want
*/
}
You can use the above way or you can read the input and overwrite the '\n' to '\0' in the input array and compare as it is you before done. But you have to use the strcmp. Because the array size is incremented.
This is the right way for using the fgets. Use of fgets is to read upto new line.
You have to use the null character for the character array. Because this is necessary for the character arrays.
Your initiative towards using strcmp() and fgets() is good, though, it requires following understanding:
1. fgets() writes atmost size-1 characters into buffer and then terminates with '\0'. In your case,
fgets(input,7, stdin);
You gave input "Encrypt"/"Decrypt"/"TurnOff"
but
'input' buffer got data as "Encryp"/"Decryp"/"TurnOf"
because of size=7 (only (7-1)=6 characters being read, last position reserved for '\0' character by fgets()).
Your strncmp() calls will work correctly with your current code, since for strncmp(), length to compare
n = strlen(input) = 6;
6 characters are matching fine in all three cases of "Encrypt"/"Decrypt"/"TurnOff".
Summary is that your current code will work fine, But your actual intention is violated. You actually wanted to read and compare full length of option string.
EDIT DONE : Modifications suggested:
#define SIZE 9 <-- EDIT : Change done here, instead of 7, size = 9 is used
to allow reading '\n' so that it does not affect
fgets() read in successive iteration
char input[SIZE];
fgets(input, SIZE, stdin); // read str is e.g. "Encrypt\n"
input[SIZE-2] = '\0'; // To replace '\n' with '\0'
Similarly, you need to be careful when reading into 'message' array using fgets().
This program essentially asks for a secret string, then asks a user to repeatedly guess single chars of that string until he guesses it all. It works however every second time the while loop is run it skips user input for the guessed char. How do I fix this?
int main(){
char guess;
char test2 [50];
char * s = test2;
char output [50];
char * t = output;
printf("Enter the secret string:\n");
fgets(test2, 50, stdin);
for (int i=0;i<49;i++){ //fills ouput with _ spaces
*(output +i)='_';
while(strcmp(s,t) != 0){
printf("Enter a guess:");
scanf("%c",&guess);
printf("You entered: %c\n", guess);
showGuess(guess,s, t ); // makes a string "output" with guesses in it
printf("%s\n",t);
}
printf("Well Done!");
}
For a quick and dirty solution try
// the space in the format string consumes optional spaces, tabs, enters
if (scanf(" %c", &guess) != 1) /* error */;
For a better solution redo your code to use fgets() and then parse the input.
As pointed out in some other answers and comments, you need to "consume" the "newline character" in the input.
The reason for that is that the input from your keyboard to the program is buffered by your shell, and so, the program won't see anything until you actually tell your shell to "pass the content of its buffer to the program". At this point, the program will be able to read the data contained in the previous buffer, e.g. your input, followed by one the character(s) used to validate your input in the shell: the newline. If you don't "consume" the newline before you do another scanf, that second scanf will read the newline character, resulting in the "skipped scanf" you've witnessed. To consume the extra character(s) from the input, the best way is to read them and discard what you read (what the code below does, notice the
while(getc(stdin) != '\n');
line after your scanf. What this line does is: "while the character read from stdin is not '\n', do nothing and loop.").
As an alternative, you could tell your shell to not buffer the input, via the termios(3) functions, or you could use either of the curses/ncurses libraries for the I/O.
So here is what you want:
int main(){
char guess;
char test2 [50];
char * s = test2; // 3. Useless
char output [50];
char * t = output; // 3. Useless
int i; // 8. i shall be declared here.
printf("Enter the secret string:\n");
fgets(test2, 50, stdin);
for (i=0;i<50;i++) if (test2[i] == '\n') test2[i] = '\0'; // 4. Remove the newline char and terminate the string where the newline char is.
for (int i=0;i<49;i++){ // 5. You should use memset here; 8. You should not declare 'i' here.
*(output +i)='_';
} // 1. Either you close the block here, or you don't open one for just one line.
output[49] = '\0'; // 6. You need to terminate your output string.
while(strcmp(s,t) != 0){ // 7. That will never work in the current state.
printf("Enter a guess:");
scanf("%c",&guess);
while(getc(stdin) != '\n');
printf("You entered: %c\n", guess);
showGuess(guess,s, t );
printf("%s\n",t);
}
printf("Well Done!");
return 0; // 2. int main requires that.
}
Other comments on your code:
You opened a block after your for loop and never closed it. That might be causing problems.
You declared your main as a function returning an integer... So you should at least return 0; at the end.
You seem to have understood that char * t = output; copies output's value and uses t as a name for the new copy. This is wrong. You are indeed copying something, but you only copy the address (a.k.a reference) of output in t. As a result, output and t refer to the same data, and if you modify output, t will get modified; and vice versa. Otherwise said, those t and s variables are useless in the current state.
You also need to remove the newline character from your input in the test2 buffer. I have added a line after the fgets for that.
Instead of setting all the bytes of an array "by hand", please consider using the memset function instead.
You need to actually terminate the output string after you "fill" it, so you should allocate a '\0' in last position.
You will never be able to compare the test2 string with the output one, since the output one is filled with underscores, when your test2 is NULL terminated after its meaningful content.
While variables at the loop scope are valid according to C99 and C11, they are not standard in ANSI C; and it is usually better to not declare any variable in a loop.
Also, "_ spaces" are called "underscores" ;)
Here is a code that does what you want:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LEN 50
int main()
{
char phrase[LEN];
char guessed[LEN];
char guess;
int i, tries = 0;
puts("Please enter the secret string:");
if(fgets(phrase, LEN, stdin) == NULL)
return 1;
for(i = 0; i < LEN && phrase[i] != '\n'; i++); // Detect the end of input data.
for(; i < LEN; i++) // For the rest of the input data,
phrase[i] = '_'; // fill with underscores (so it can be compared with 'guessed' in the while loop).
phrase[LEN - 1] = '\0'; // NULL terminate 'phrase'
memset(guessed, '_', LEN); // Fill 'guessed' with underscores.
guessed[LEN - 1] = '\0'; // NULL terminate 'guessed'
while(strcmp(phrase, guessed) != 0) // While 'phrase' and 'guessed' differ
{
puts("Enter a guess (one character only):");
if(scanf("%c", &guess) != 1)
{
puts("Error while parsing stdin.");
continue;
}
if(guess == '\n')
{
puts("Invalid input.");
continue;
}
while(getc(stdin) != '\n'); // "Eat" the extra remaining characters in the input.
printf("You entered: %c\n", guess);
for(i = 0; i < LEN; i++) // For the total size,
if(phrase[i] == guess) // if guess is found in 'phrase'
guessed[i] = guess; // set the same letters in 'guessed'
printf("Guessed so far: %s\n", guessed);
tries++;
}
printf("Well played! (%d tries)\n", tries);
return 0;
}
Feel free to ask questions in the comments, if you are not getting something. :)
Newline character entered in the previous iteration is being read by scanf. You can take in the '\n' by using the getc() as follows:
scanf("%c",&guess);
getc(stdin);
..
This changed worked for me. Though the right explanation and c leaner code is the one given by #7heo.tk
Change
scanf("%c",&guess);
with
scanf(" %c",&guess);
It should ignore '\n'.
So I have a program that takes user input and compares it to a specific line in a file, however the final line will always be credited as incorrect, so can someone solve this for me?, thanks.
File content (just a list of random words)
Baby
Milk
Car
Face
Library
Disc
Lollipop
Suck
Food
Pig
(libraries are stdio,conio and string)
char text[100], blank[100];
int c = 0, d = 0;
void space(void);
int main()
{
int loop = 0;
char str[512];
char string[512];
int line = 1;
int dis = 1;
int score = 0;
char text[64];
FILE *fd;
fd = fopen("Student Usernames.txt", "r"); // Should be test
if (fd == NULL)
{
printf("Failed to open file\n");
exit(1);
}
do
{
printf("Enter the string: ");
gets(text);
while (text[c] != '\0')
{
if (!(text[c] == ' ' && text[c] == ' '))
{
string[d] = text[c];
d++;
}
c++;
}
string[d] = '\0';
printf("Text after removing blanks\n%s\n", string);
getch();
for(loop = 0;loop<line;++loop)
{
fgets(str, sizeof(str), fd);
}
printf("\nLine %d: %s\n", dis, str);
dis=dis+1;
str[strlen(str)-1] = '\0';
if(strcmp(string,str) == 0 )
{
printf("Match\n");
score=score+2;
}
else
{
printf("Nope\n");
score=score+1;
}
getch();
c=0;
d=0;
}
while(!feof(fd));
printf("Score: %d",score);
getch();
}
For any input on the last line, the output will always be incorrect, I believe this is something to do with the for loop not turning it into the next variable, but seeing as the <= notation makes this program worse, I really just need a simple fix for the program thanks.
Some observations:
You must never use gets (it is not even in the C11 standard anymore). Instead of gets(text) use fgets(text, sizeof(text), stdin) – this way a long input will not overflow the text array.
There will be stuff printed at the end because you don't check the return value of either the gets or the fgets, so when end of file occurs for either the file or for user input the rest of that iteration still runs. fgets returns NULL if it didn't read anything – check for that instead of using feof.
You remove newlines from the file input but not from the user input, so the comparison will always fail when you switch from gets to fgets (which doesn't strip linefeeds). The second (otherwise pointless) comparison of text[c] against ' ' should be against '\n'.
edit: Also, in case the last line of your file does not end in a linefeed, the comparison will fail on the last line because you don't check if the last character is a linefeed before you remove it.
The for (loop = 0; loop < line; ++loop) -loop is pointless because line is always 1, so the body is only executed once.
You have unnecessarily global variables which the program hard to follow. And, for instance, your local text[64] overshadows the global text[100], so if you think you are modifying the global buffer, you are not. If your code is complete, none of the variables should be global.
The function getch() is non-standard. There is no easy direct replacement, so you may just accept that you are not writing portable code, but it's something to be aware of.
This question already has an answer here:
C file handling query
(1 answer)
Closed 9 years ago.
So I have a program that takes user input and compares it to a line in the file, before going down onto the next line, if the user gets the word right they get 2 points, if it's wrong they get 1 point. As a failsafe I have added a small function within the program that will take out any spaces from the word.
The program works as expected, the spaces are removed and when run all words are scanned and compared effectively.
HOWEVER, once on the last line of the file, the correct spelling of the word will give the wrong output, this might have something to do with the loops but I'm not sure.
In a nutshell: All I need is one of you talented programmers out there to take a look at my code and see what's causing this to happen.
File content (just a list of random words)
Baby
Milk
Car
Face
Library
Disc
Lollipop
Suck
Food
Pig
(libraries are stdio,conio and string)
char text[100], blank[100];
int c = 0, d = 0;
void space(void);
int main()
{
int loop = 0;
char str[512];
char string[512];
int line = 1;
int dis = 1;
int score = 0;
char text[64];
FILE *fd;
fd = fopen("Student Usernames.txt", "r"); // Should be test
if (fd == NULL)
{
printf("Failed to open file\n");
exit(1);
}
do
{
printf("Enter the string: ");
gets(text);
while (text[c] != '\0')
{
if (!(text[c] == ' ' && text[c] == ' '))
{
string[d] = text[c];
d++;
}
c++;
}
string[d] = '\0';
printf("Text after removing blanks\n%s\n", string);
getch();
for(loop = 0;loop<line;++loop)
{
fgets(str, sizeof(str), fd);
}
printf("\nLine %d: %s\n", dis, str);
dis=dis+1;
str[strlen(str)-1] = '\0';
if(strcmp(string,str) == 0 )
{
printf("Match\n");
score=score+2;
}
else
{
printf("Nope\n");
score=score+1;
}
getch();
c=0;
d=0;
}
while(!feof(fd));
printf("Score: %d",score);
getch();
}
For any input on the last line, the output will always be incorrect, I believe this is something to do with the for loop not turning it into the next variable, but seeing as the <= notation makes this program worse, I really just need a simple fix for the program thanks.
P.S. For anyone who is going to comment about my coding for the spaces function, Yes, I could make it better, but it's not a problem right now. So please don't write anything concerning it.
I guess your file is not terminated by a newline. So the last word, Pig, gets truncated by this line of code:
str[strlen(str)-1] = '\0';
(which is unconditional).
Either put a newline at the end of your file, or check the end-of-string before truncating:
if (isspace(str[strlen(str)-1]))
str[strlen(str)-1] = '\0';
(Might also use strtok to remove all whitespace from the string without writing tricky code)
You need to check if the last character is a linefeed in both the user input and the line read from the file, and remove it if and only if it is. (You also need to fix the other bugs, such as the use of gets and feof, and not all the changes can be done in isolation because some of your bugs depend on one another so fixing only one will break it until you fix the others.)
I'm trying to save certain words into a pointer to an array (**valid_words), but I am running into an issue I do not understand. Below is the function I am trying to utilize, end result is to have valid_words contains all the valid words according to the condition when iline and dline are equal.
void CheckFile(char *input_file, char *dictionary_file, char *output_file, char **valid_words)
{
/* Open files as read or append */
FILE *input, *dictionary, *output;
input = fopen(input_file, "r");
dictionary = fopen(dictionary_file, "r");
output = fopen(output_file,"a");
int word_count = 0;
/* Read from files */
if (input != NULL && dictionary!= NULL)
{
char *iline = malloc(MAXSIZE*sizeof(char));
char *dline = malloc(MAXSIZE*sizeof(char));
while (fgets (iline, sizeof iline, input) != NULL)
{
while (fgets (dline, sizeof dline, dictionary) != NULL)
{
trimwhitespace(iline);
trimwhitespace(dline);
if (strcasecmp(iline, dline) == 0 )
{
fprintf(output, "%s\n",iline);
valid_words[word_count] = iline;
printf("Word Count: %d\n", word_count);
printf("At Zero: %s\n", valid_words[0]);
printf("Valid Word: %s\n", valid_words[word_count]);
printf("Actual Word: %s\n", iline);
word_count++;
}
}
rewind(dictionary);
}
fclose(input);
fclose(dictionary);
fclose(output);
free(iline);
free(dline);
}
else
{
printf("An error has occured\n");
}
}
The current output I am getting is
Word Count: 0
At Zero: miles
Valid Word: miles
Actual Word: miles
Word Count: 1
At Zero: limes
Valid Word: limes
Actual Word: limes
Word Count: 2
At Zero: smile
Valid Word: smile
Actual Word: smile
Word Count: 3
At Zero: slime
Valid Word: slime
Actual Word: slime
I am expecting At Zero: to always output "miles", but this is not happening.
After the function has been called printing valid_words[i] results in nothing being printed out.
I will really appreciate it if someone can help me out, and am very open to any criticism. You can find the full implementation here at http://pastebin.com/TjxLRVaC
Thank you.
The answer to why it doesn't work is -
valid_words[word_count] = iline;
You should use either strcpy after ensuring that the element in valid_words can hold up to MAXSIZE characters or use strncat (NB: not strncpy) after ensuring that the first (really zeroth) character in the element in valid_words that you are inserting is '\0'.
Some observations:
Assuming MAXSIZE is a #defined constant, this is better -
char iline[MAXSIZE];
char dline[MAXSIZE;
than the malloc calls that you have (by the way, sizeof(char) is one by definition.)
Likewise, you are better off changing the last parameter of CheckFile to char valid_words[][MAXSIZE] (and thus changing the arguments in all the calls to CheckFile).
Hope this helps.