Am I using strncmp and fgets in the right way? - c

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().

Related

C - Is there a way to read a single character of user input, and not have the rest "pushed down" to the next request for input?

So, I'm working on a simple hangman game in C, and I have the function read_guess, shown below.
void read_guess(char *guesses, char *p_current_guess)
{
int valid_guess = 0;
// Repeatedly takes input until guess is valid
while (valid_guess == 0)
{
printf(">>> ");
fgets(p_current_guess, 2, stdin);
if (!isalpha(*p_current_guess)) printf("Guesses must be alphabetic. Please try again.\n\n");
else
{
valid_guess = 1;
// Iterates over array of guesses and checks if letter has already been guessed
for (int i = 0; guesses[i] != '\0'; i++)
{
if (guesses[i] == *p_current_guess)
{
printf("You have already guessed this letter. Please try again.\n\n");
valid_guess = 0;
break;
}
}
}
}
}
I've tried all the standard input functions (including getchar), but with all of them, when an input larger than one character is supplied, instead of taking just the first character and moving on (or asking again), the rest of the input is "pushed back", and the next time input is requested, whether it be because the input contained a non-alphabetic character or the next round begins, the rest of the input is automatically processed. This repeats for each character of the input.
How can I avoid this?
You are using fgets which is good, but unfortunately not the right way...
fgets reads up to an end of line or at most 1 less the the number of character asked. And of course remaining characters are left for the next read operation...
The idiomatic way would be to ensure reading up to the end of line, whatever the length, or at least up to a much larger length.
Simple but could fail in more than SIZE characters on input:
#define SIZE 64
...
void read_guess(char *guesses, char *p_current_guess)
{
char line[SIZE];
int valid_guess = 0;
// Repeatedly takes input until guess is valid
while (valid_guess == 0)
{
printf(">>> ");
fgets(line, SiZE, stdin); // read a line of size at most SIZE-1
p_current_guess[0] = line[0]; // keep first character
p_current_guess[1] = '\0';
...
Robust but slightly more complex
/**
* Read a line and only keep the first character
*
* Syntax: char * fgetfirst(dest, fd);
*
* Parameters:
* dest: points to a buffer of size at least 2 that will recieve the
* first character followed with a null
* fd : FILE* from which to read
*
* Return value: dest if one character was successfully read, else NULL
*/
char *readfirst(dest, fd) {
#define SIZE 256 // may be adapted
char buf[SIZE];
char *cr = NULL; // return value initialized to NULL if nothing can be read
for (;;) {
if(NULL == fgets(buff, sizeof(buff), fd)) return cr; // read error or end of file
if (0 == strcspn(buff, "\n")) return cr; // end of file
if (cr == NULL) { // first read:
cr = dest; // prepare to return first char
dest[0] = buff[0];
dest[1] = 0;
}
}
}
You can then use it simply in your code:
void read_guess(char *guesses, char *p_current_guess)
{
int valid_guess = 0;
// Repeatedly takes input until guess is valid
while (valid_guess == 0)
{
printf(">>> ");
fgetfirst(p_current_guess, stdin);
You can discard all input until end-of-line, each time you want to ask for input.
void skip_to_eol(FILE* f, int c)
{
while (c != EOF && c != '\n')
c = fgetc(f);
}
...
char c = getchar(); // instead of fgets
skip_to_eol(stdin, c);
You can use getch() function on windows to get single character. and this is linux equivalent
What is the equivalent to getch() & getche() in Linux?

C: Break Loop on Empty Input

So I'm writing a program that will loop forever, accepting string inputs until the user just presses enter with no string (along the way, I'm tracking the longest/shortest strings entered). I have this loop:
char stringIn[1000] = {'\0'};
while(1) {
scanf("%[^\n]s", stringIn);
if(stringIn[0] == '\0') {
break;
}
if(strlen(stringIn) > strlen(longString)) {
longString == stringIn;
} else if (strlen(stringIn) < strlen(shortString)) {
shortString == stringIn;
}
i++;
}
Currently this just loops forever. I'm still really new to C, but to me this looks like it should've worked.
Points to note:
You probably mistook the == operator for =, which is assignment. Even so, it wouldn't work because here it would only copy addresses of buffers (which get overwritten) (actually in my code it would throw a compile time errors). For copying strings you wanna use strcpy.
scanf is pretty vulnerable to buffer overflows and leaves the delimiter in the buffer. fgets is a much better choice for reading lines as it takes a buffer length as argument (check this out).
scanf fills a number of items in it's list until characters matching the format string are read. If no chars match, then it doesn't fill stringIn, and hence doesn't append a '\0' at the end, and that's why your code never goes to break;. Instead we can use the return value, which is the number of items of the list that it fills (see here).
Anyway, here is code that does what you want:
int main() {
char stringIn[1000] = "";
char longString[2000] = "", shortString[2000] = "";
int read, firstFlag = 0;
while(1) {
read = scanf("%[^\n]", stringIn);
if (read == 0) {
break;
}
// to consume the '\n' left by scanf in the buffer
getchar();
if (!firstFlag || strlen(stringIn) > strlen(longString)) {
strcpy(longString, stringIn);
}
if (!firstFlag || strlen(stringIn) < strlen(shortString)) {
strcpy(shortString, stringIn);
}
firstFlag = 1;
}
printf("%s, %s\n", longString, shortString);
return 0;
}
UPDATE: Edited according to Jonathan Leffler's comment above, correcting the use of the scanset.

scanf isn't executing on every loop iteration

I am writing a program for fun (not for school), and am having a hard time figuring out why the scanf function isn't executing on every iteration of my loop - I've toyed with both 'for' loops and 'while' loops.
I know that depending on how I write the scanf function (i.e. scanf("%s", &variablename); VS scanf("%99[^\n]s", &variablename);) makes a difference, but I have tried everything and I'm desperate!
When I do a printf check on my input from the scanf, on every iteration it is only intaking one string per iteration, so if I enter two words in my first input, then it takes two iterations to process - one word per. Here is the segment of code I'm describing:
int main(void){
int tries = 0;
int score = 0;
char question[100];
char useranswer[100];
const char *phrase = {"our favorite saying\0"};
printf("\nQuestion #3 (10 points): What is our secret saying?\n");
sleep(1);
tries = 1;
while (tries<=3){
printf("YOUR ANSWER:");
scanf("%s[^\n]", useranswer);
if(strncmp(useranswer, phrase, 15) != 0){
printf ("Nope, try again!\n");
printf("You have used %d out of 3 tries!\n", tries);
if (tries == 2){
printf("Here's your final hint:xxx...\n");
}
if (tries == 3){
printf("You didn't get it. The answer is: our favorite saying!\n");
}
tries++;
}
if (strncmp(useranswer, phrase, 15) == 0){
printf("Damn, you're good. Well done.\n");
score += 10;
break;
}
}
The output of this code is:
Question #3 (10 points): What is our secret saying?
YOUR ANSWER:our favorite saying
Nope, try again!
You have used 1 out of 3 tries!
YOUR ANSWER:Nope, try again!
You have used 2 out of 3 tries!
Here's your final hint:xxx...
YOUR ANSWER:Nope, try again!
You have used 3 out of 3 tries!
You didn't get it. The answer is: our favorite saying!
(It only allowed me to input once, and I typed "our favorite saying".)
In comments you could find why your format specifier in scanf doesn't work.
An alternative is to use fgets instead, maybe in an helper function which takes care of some of the corner cases that can arise while reading user input:
#include <ctype.h>
char *read_line( char *buf, size_t n, FILE *pfin )
{
ssize_t length = 0;
int ch;
if ( !buf || n == 0 )
return NULL;
/* Consume trailing control characters, like '\0','\n', '\r', '\f'...
but also '\t'. Note that ' ' is not skipped. */
while ( (ch = fgetc(pfin)) != EOF && iscntrl(ch) ) { }
if ( ch == EOF )
return NULL;
/* At least one char is printable */
*buf = ch;
++length;
/* Read from file till a newline or up to n-2 chars. The remaining chars
are left in the stream buffer. Return NULL if no char is read. */
if ( fgets(buf + 1, n - 1, pfin) )
{
/* Trim the string to the first control character */
while ( !iscntrl(buf[length]) )
{
++length;
}
buf[length] = '\0';
}
return buf;
}
I'd change the following logic too. OP uses strncmp(useranswer, phrase, 15) multiple times, but that magic number 15 is lower then phrase's size so it ends up comparing only a substring.
while ( tries <= 3 ) {
printf("YOUR ANSWER:");
if ( !read_line(useranswer, sizeof useranswer, stdin) ) {
printf("Error: Unexpected end of input.\n");
exit(EXIT_FAILURE);
}
if( strcmp(useranswer, phrase) == 0 ) {
printf("Damn, you're good. Well done.\n");
score += 10;
break;
} else {
printf ("Nope, try again!\n");
printf("You have used %d out of 3 tries!\n", tries);
if (tries == 2) {
printf("Here's your final hint:xxx...\n");
}
if (tries == 3) {
printf("You didn't get it. The answer is: our favorite saying!\n");
}
tries++;
}
}
As a final note, I found OP declaration of phrase a bit weird (maybe a typo):
const char *phrase = {"our favorite saying\0"};
// string literals are already ^^ null terminated...
While we can use a simple array declaration, like:
const char phrase[] = "our favorite saying";
Consider also what values sizeof phrase returns in those two different cases.
Thanks to #chux for all the valuable hints and the interesting links provided:
https://stackoverflow.com/a/27729970/4944425
https://stackoverflow.com/a/28462221/4944425
And to #Dmitri for having pointed out in his comment that once we are sure that both the strings are null terminated, we can use strcmp instead of strncmp.

Scanf skipped in loop (Hangman)

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'.

scanf() in a loop

I am trying to make a program where the computer has to guess a number between 0 and 100 picked by the user(me). The program should guess the number within 7 tries which it does, but I'm having a problem with scanf(). When the program tries to guess the number, the user must tell it if the number guessed is too high, too low, or correct. The program works fine when the user just types a one character response, but freaks out when there is more than one character in "response". So I would like to limit the response to just one character. How do I do this?
Thanks
#include <stdio.h>
#include <string.h>
int main() {
char response[1];
int numOfGuesses = 1;
int min = 0;
int max = 100;
int guess = (max+min)/2;
int end = 0;
do {
printf("%d) %d: (h)igh, (l)ow, or (c)orrect? ", numOfGuesses, guess);
if (scanf("%1s", response) == 1) {
printf("%s \n", response);
if (strlen(response) > 1) {
printf("D'oh! Wrong response!! \n");
} else {
if (response[0] == 'h') {
max = guess;
numOfGuesses++;
} else if (response[0] == 'l') {
min = guess;
numOfGuesses++;
} else if (response[0] == 'c') {
printf("Correct! It took %d turns. \n", numOfGuesses);
end = 1;
} else {
printf("D'oh! Wrong response! \n");
}
guess = (max+min)/2;
}
} else {
end = 1;
}
} while ( end == 0 );
return 0;
}
Your response buffer is not null terminated, which pretty much makes it explode at
if (strlen(response) > 1) {
Make it response[2].
Alternatively, you can use %c in scanf instead of %1s.
Edit:
Try calling this function after every scanf, it flushes the input buffer until the next newline, removing any invalid input after the first char.
void flush_input()
{
int c = 0;
do
{
c = getchar();
} while (c != EOF && c != '\n');
}
If you're just looking for a single character input, why not use getchar() instead of scanf()?
Here's a page that discusses getchar() in more detail and has some usage examples, if you're interested: http://rabbit.eng.miami.edu/class/een218/getchar.html
Are you getting extra points for saving memory?
#include <stdio.h> up top, then declare response to be a large-ish array: char response[BUFSIZ];
You should initialize it before the loop, just because it's a good habit: *response='\0';
As you're not doing much, you could call gets(response) instead of scanf(). This will read in the entire line entered. Modern compilers and/or run-times will whine about the gets() as it can run off the end of the provided array (which is what your scanf() was doing), suggesting fgets() instead. The more-correct snippet becomes:
fgets(response, sizeof response, stdin);
use
if (scanf("%2s", response) == 1) {
instead of
if (scanf("%1s", response) == 1) {
this makes the compiler read up to 2 characters instead of just 1 at a time

Resources