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'.
Related
Hi i'm struggling with understanding what's wrong with my program.
My best guess is something related with this line of code here:
scanf("%s", str);
The thing is i'm trying to call a function that uses a strtok on a String passed on to it typed by the user, all of this inside of a while loop as shown in the code example below:
int i = 0;
char str[80];
while(i != 3){
printf("Type in some string so i can break it: ");
scanf("%s", str);
testFunc(str);
printf("Loop %i ended.\n", i);
i++;
}
return 1;
Result (not what i want, see further below what i actually want):
Type in some string so i can break it: hey there how are you doing!
hey
Loop 0 ended.
Type in some string so i can break it:
there
Loop 1 ended.
Type in some string so i can break it:
how
Loop 2 ended.
The reason why i think this is caused by the scanf line is because the program works fine when i'm using instead some dummy pre-declared String
int i = 0;
while(i != 3){
char str[80] = "hey there how are you doing!";
testFunc(str);
printf("Loop %i ended.\n", i);
i++;
}
return 1;
Result:
hey
there
how
are
you
doing!
Loop 0 ended.
hey
there
how
are
you
doing!
Loop 1 ended.
hey
there
how
are
you
doing!
Loop 2 ended.
Here's the funtion that uses strtok, most of the code here is taken from https://www.tutorialspoint.com/c_standard_library/c_function_strtok.htm
int testFunc(char linha[80]){
//
const char s[2] = " ";
char *token;
/* get the first token */
token = strtok(linha, s);
/* walk through other tokens */
while(token != NULL) {
printf("%s\n", token);
token = strtok(NULL, s);
}
return 1;
}
I'm puzzled, it's like the program is executing testFunc() in paralel with the main function.
I think the problem is when you execute this loop:
while(i != 3){
printf("Type in some string so i can break it: ");
scanf("%s", str);
testFunc(str);
printf("Loop %i ended.\n", i);
i++;
}
scanf gets only one word at a time, so the loop cycles 3 times, you only get 3 words no matter how long the input string is.
On the other hand, in your other example you already have a string to break apart so the function will work.
There are different ways to get spaced strings from the console but here is what I consider to be a good option to do it:
str[MAX_SIZE];
fgets(str, MAX_SIZE, stdin);
// where MAX_SIZE is the maximum size you want to allow for the string,
//must be smaller than str.
According to scanf(3) man page:
Matches a sequence of non-white-space characters; the next
pointer must be a pointer to the initial element of a
character array that is long enough to hold the input sequence
and the terminating null byte ('\0'), which is added
automatically. The input string stops at white space or at
the maximum field width, whichever occurs first
You can use fgets or fread for input:
char buffer[1000];
/* fgets (reads a line of text with trailing newline */
fgets (buffer, 1000, stdin);
int main(void)
{
char ch;
int len = 0;
printf("Enter a message: ");
ch = getchar();
while (ch != '\n')
{
len = len + 1;
ch = getchar();
}
printf("Your message was %d characters long\n", len);
return 0;
}
I understand the first " ch = getchar(); " because that's reading the characters from the printf statement, but I don't understand the purpose of the 2nd " ch = getchar(); " in the while loop.
This is trying to loop on reading characters until a newline is read, and it's just doing it in an odd way: normally I'd expect to see just one getchar() as part of the loop itself.
This would be a more customary way to write the same thing:
#include <stdio.h>
int main(void)
{
int ch; // note: not char!
int len = 0;
printf("Enter a message: ");
while ( (ch = getchar()) != EOF && ch != '\n')
len++;
printf("Your message was %d characters long\n", len);
return 0;
}
We see a few changes here.
First, we see the ch = getchar() assignment in the loop condition itself: fetch a character, and as long as we're not done (however defined), keep going and keep counting.
But we also have to check for EOF - end of file - which signifies the end of input, so that test is done here as well. If you fail this test, the code will loop and loop and loop and loop because it will never get EOF. Always check for end of file!
IMPORTANT: You really must use an int variable to receive the return from getchar(), not a char. Surprise! The reason is that EOF is typically -1 (EOF must be negative), and getchar() returns either EOF or the next 'character as an unsigned char converted to an int' (normally, a value 0..255). That is more values than can be stored in a plain char, so it is crucial to use an int instead of a char variable so you can be sure to detect EOF properly.
Also a trivial simplification of len++ for len = len + 1.
The original loop is doing more or less the same thing, but without combining the get + check parts into one.
To get all the characters that are typed before pressing enter.
getchar reads only one character at a time.
The second ch = getchar reads one character, but it is inside of a while loop so getchar()will be read characters from input, one by one, until a line-break appears in the input. If you put in the input a string like this "Hello World" and press enter your program will read "Hello world\n" then the while loop increase by one the variable len to count the characters which you putted in the input and when getcharreads '\n' the while loop will end and the printf()shows you the length of your string
I was trying to take a full line input in C. Initially I did,
char line[100] // assume no line is longer than 100 letters.
scanf("%s", line);
Ignoring security flaws and buffer overflows, I knew this could never take more than a word input. I modified it again,
scanf("[^\n]", line);
This, of course, couldn't take more than a line of input. The following code, however was running into infinite loop,
while(fscanf(stdin, "%[^\n]", line) != EOF)
{
printf("%s\n", line);
}
This was because, the \n was never consumed, and would repeatedly stop at the same point and had the same value in line. So I rewrote the code as,
while(fscanf(stdin, "%[^\n]\n", line) != EOF)
{
printf("%s\n", line);
}
This code worked impeccably(or so I thought), for input from a file. But for input from stdin, this produced cryptic, weird, inarticulate behavior. Only after second line was input, the first line would print. I'm unable to understand what is really happening.
All I am doing is this. Note down the string until you encounter a \n, store it in line and then consume the \n from the input buffer. Now print this line and get ready for next line from the input. Or am I being misled?
At the time of posting this question however, I found a better alternative,
while(fscanf(stdin, "%[^\n]%*c", line) != EOF)
{
printf("%s\n", line);
}
This works flawlessly for all cases. But my question still remains. How come this code,
while(fscanf(stdin, "%[^\n]\n", line) != EOF)
{
printf("%s\n", line);
}
worked for inputs from file, but is causing issues for input from standard input?
Use fgets(). #FredK
char buf[N];
while (fgets(buf, sizeof buf, stdin)) {
// crop potential \n if desired.
buf[strcspn(buf, "\n")] = '\0';
...
}
There are to many issues trying to use scanf() for user input that render it prone to mis-use or code attacks.
// Leaves trailing \n in stdin
scanf("%[^\n]", line)
// Does nothing if line begins with \n. \n remains in stdin
// As return value not checked, use of line may be UB.
// If some text read, consumes \n and then all following whitespace: ' ' \n \t etc.
// Then does not return until a non-white-space is entered.
// As stdin is usually buffered, this implies 2 lines of user input.
// Fails to limit input.
scanf("%[^\n]\n", line)
// Does nothing if line begins with \n. \n remains in stdin
// Consumes 1 char after `line`, even if next character is not a \n
scanf("%99[^\n]%*c", line)
Check against EOF is usual the wrong check. #Weather Vane The following, when \n is first entered, returns 0 as line is not populated. As 0 != EOF, code goes on to use an uninitialized line leading to UB.
while(fscanf(stdin, "%[^\n]%*c", line) != EOF)
Consider entering "1234\n" to the following. Likely infinite loop as first fscanf() read "123", tosses the "4" and the next fscanf() call gets stuck on \n.
while(fscanf(stdin, "%3[^\n]%*c", line) != EOF)
When checking the results of *scanf(), check against what you want, not against one of the values you do not want. (But even the following has other troubles)
while(fscanf(stdin, "%[^\n]%*c", line) == 1)
About the closest scanf() to read a line:
char buf[100];
buf[0] = 0;
int cnt = scanf("%99[^\n]", buf);
if (cnt == EOF) Handle_EndOfFile();
// Consume \n if next stdin char is a \n
scanf("%*1[\n]");
// Use buf;
while(fscanf(stdin, "%[^\n]%*c", line) != EOF)
worked for inputs from file, but is causing issues for input from standard input?
Posting sample code and input/data file would be useful. With modest amount of code posted, some potential reasons.
line overrun is UB
Input begins with \n leading to UB
File or stdin not both opened in same mode. \r not translated in one.
Note: The following fails when a line is 100 characters. So meeting the assumption cal still lead to UB.
char line[100] // assume no line is longer than 100 letters.
scanf("%s", line);
Personally, I think fgets() is badly designed. When I read a line, I want to read it in whole regardless of its length (except filling up all RAM). fgets() can't do that in one go. If there is a long line, you have to manually run it multiple times until it reaches the newline. The glibc-specific getline() is more convenient in this regard. Here is a function that mimics GNU's getline():
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
long my_getline(char **buf, long *m_buf, FILE *fp)
{
long tot = 0, max = 0;
char *p;
if (*m_buf == 0) { // empty buffer; allocate
*m_buf = 16; // initial size; could be larger
*buf = (char*)malloc(*m_buf); // FIXME: check NULL
}
for (p = *buf, max = *m_buf;;) {
long l, old_m;
if (fgets(p, max, fp) == NULL)
return tot? tot : EOF; // reach end-of-file
for (l = 0; l < max; ++l)
if (p[l] == '\n') break;
if (l < max) { // a complete line
tot += l, p[l] = 0;
break;
}
old_m = *m_buf;
*m_buf <<= 1; // incomplete line; double the buffer
*buf = (char*)realloc(*buf, *m_buf); // check NULL
max = (*m_buf) - old_m;
p = (*buf) + old_m - 1; // point to the end of partial line
}
return tot;
}
int main(int argc, char *argv[])
{
long l, m_buf = 0;
char *buf = 0;
while ((l = my_getline(&buf, &m_buf, stdin)) != EOF)
puts(buf);
free(buf);
return 0;
}
I usually use my own readline() function. I wrote this my_getline() a moment ago. It has not been thoroughly tested. Please use with caution.
I'm trying to make a function to validate mobile entry, the mobile number MUST starts with 0 and is 11 numbers (01281220427 for example.)
I want to make sure that the program gets the right entry.
This is my attempt:
#include <stdio.h>
#include <strings.h>
void integerValidation(char x[15]);
int main(int argc, char **argv)
{
char mobile[15];
integerValidation(mobile);
printf("%s\n\n\n", mobile);
return 0;
}
void integerValidation(char x[15]){
char input[15];
long int num = -1;
char *cp, ch;
int n;
printf("Please enter a valid mobile number:");
while(num<0){
cp = fgets(input, sizeof(input), stdin);
if (cp == input) {
n = sscanf(input, "%ld %c", &num, &ch);
if (n!=1) {printf("ERROR! Please enter a valid mobile number:");
num = -1;
}
else if (num<0)
printf("ERROR! Please enter a valid mobile number:");
else if ((strlen(input)-1)>11 || (strlen(input)-1)<11 || strncmp(&input[0], "0", 1) != 0){
printf("ERROR! Please enter a valid mobile number:");
num = -1;
}
}
}
long int i;
i = strlen(input);
//Because when I try to print it out it prints a line after number.
strcpy(&input[i-1], "");
strcpy(x, input);
}
Now, if I don't use
strcpy(&input[i-1], "");
the array prints a new line after the number, what would be a good fix other than mine? and how can I make this function optimized and shorter?
Thanks in advance!
Edit:
My question is: 1. Why does the input array prints a new line in the end?
2. How can I make this code shorter?
End of edit.
If you insist on using sscanf(), you should change the format this way:
int integerValidation(char x[15]) {
char input[15], c;
printf("Please enter a valid mobile number:");
while (fgets(input, sizeof(input), stdin)) {
if (sscanf(input, "%11[0123456789]%c", x, &c) == 2
&& x[0] == '0' && strlen(x) == 11 && c == '\n') {
// number stored in `x` is correct
return 1;
}
printf("ERROR! Please enter a valid mobile number:");
}
x[0] = '\0'; // no number was input, end of file reached
return 0;
}
%12[0123456789] parses at most 11 characters that must be digits.
%c reads the following character, which should be the trailing '\n'.
I verify that both formats have been matched, and the number starts with 0 (x[0] == '0') and it has exactly 11 digits.
You're seeing the newline, since fgets() reads until an EOF or a newline is received. The newline is stored in the buffer, and after that the string is terminated with '\0'.
An alternative would be to directly overwrite the newline with another null-byte: input[i-1] = '\0' (which basically does the same thing as your solution, but saves a function call).
The same goes for the check with strncmp with length 1, you can directly check input[0] == '0'. Note that you have to compare against '0' (char) here, not "0" (string).
A few other things I'm seeing:
You can also spare the %c in the format string for sscanf (you're never evaluating it anyway, since you're checking for 1 as return value), which also eliminates the need for char ch.
Also, you're passing char x[15] as argument to your function. This is a bit misleading, because what actually gets passed is a pointer to a char array (try using sizeof(x), your compiler will most likely issue a warning about the size of char * being returned by sizeof()).
What you could do is to ditch the char array input, which you're using as temporary buffer, and use the buffer which was handed over as argument. For this to be save, you should use a second funcion parameter to specify the size of the buffer which was handed to the function, which would result in a function header like as follows:
void integerValidation(char *input, size_t len);
With this, you'd have to use len instead of sizeof(input). The following question provides more detail why: C: differences between char pointer and array
Since you're not using a temporary buffer anymore, you can remove the final call to strcpy().
There are also a lot of checks for the number length/format. You can save a few:
If you use %lu instead of %ld no signed numbers are being converted, which saves you the check for num < 0.
You're checking whether the length of the read number is <11 or >11 - why not just check for !=11?
You're calling strlen() three times on the input-buffer (or still twice with the reworked check for lengh 11) - it makes sense to call it once, save the length in a variable and use that variable from then on, since you're not altering the string between the calls.
There is already an accepted answer, but for what it's worth, here is another.
I made several changes to your code, firstly avoiding "magic numbers" by defining the phone number length and an arbitrarily greater string length. Then there is no point passing an array x[15] to a function since it pays no regard to its length, might as well use the simpler *x pointer. Next, I return all reasons for failure back to the caller, that's simpler. And instead of trying to treat the phone number as a numeric entry (note: letters, spaces, hyphens, commas and # can sometimes be a part of phone number too) I stick to a character string. Another reason is that the required leading zero will vanish if you convert the entry to an int of some size. I remove the trailing newline that fgets() reads with the input line, and the result is this.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXLEN 11
#define STRLEN (MAXLEN+10)
int integerValidation(char *x);
int main(int argc, char **argv)
{
char mobile[STRLEN];
while (!integerValidation(mobile)) // keep trying
printf("Invalid phone number\n");
printf("%s\n\n\n", mobile); // result
return 0;
}
int integerValidation(char *x)
{
int i, len;
printf("Please enter a valid mobile number:");
if(fgets(x, STRLEN, stdin) == NULL) // check bad entry
return 0;
x [ strcspn(x, "\r\n") ] = 0; // remove trailing newline etc
if((len = strlen(x)) != MAXLEN) // check length
return 0;
if(x[0] != '0') // check leading 0
return 0;
for(i=1; i<len; i++) // check all other chars are numbers
if(!isdigit(x[i]))
return 0;
return 1; // success
}
so in this database I need to have certain limitations like the Name only letters and so.
but if I use the scanf("%s, c.name) for e.g., it only reads up until to the first space so if
I had to add 2 names like Marie Claire, I cannot use scanf.
I have a method which checks whether the string contains a digit or not (will provide it) but I was wondering if I could make the fgets() accept only the letters. I'll provide what I tried as well, but when I used this method, it's not accepting any input just skips it.
printf ("\nPlease enter Name:\n");
while (fgets (c.name, sizeof (c.name), stdin) && cCheck(c.name,100) == FALSE);
{
}
the method to check for digits [boolean is made with typedef from my side]
boolean cCheck(char *test, int max)
{
int x;
for (x =0; x<max; x++)
{
if (isdigit(test[x]))
{
return FALSE;
}
if (x==max)
{
return TRUE;
}
x++;
}
return TRUE;
}
Like the code sample below, you can specify fscanf to read only characters and also a . and ' ' (blank space) if you specify the format specifier on what are the acceptable characters to read. If you put a ^ on the front like [^a-zA-Z. ], then it will read everything but those characters.
#include <stdio.h>
int main()
{
char s[25];
printf("enter a string: ");
scanf("%25[a-zA-Z. ]c\n", s); // reads upto 25 chars of a-z/A-Z and '.', ' '.
printf("s1: %s\n", s);
return 0;
}
Example output from the above code:
c:\my-src\test-programs>scanf-test.exe
enter a string: dasdasd asdasda 34534536
s1: dasdasd asdasda
c:\my-src\test-programs>scanf-test.exe
enter a string: werfdsfsd3423524525
s1: werfdsfsd
See the following links for more details:
fscanf
INFO: scanf() Format Specifications and Syntax
but if I use the scanf("%s, c.name) for e.g., it only reads up until
to the first space so if I had to add 2 names like Marie Claire, I
cannot use scanf.
you can write
scanf( "%s %s", c.name, c.surname ); // better with s_scanf
I have a method which checks whether the string contains a digit or
not (will provide it) but I was wondering if I could make the fgets()
accept only the letters.
No, fgets reads characters up to and including \n there is nothing you can do about that. Instead use that to your advantage by reading the characters into a buffer and extracting the names e.g. with sscanf() or even strtok()
again recommending use of _s versions of functions to avoid nasty surprises.
In short no. But it's nearly always better to separate the input and the checking - so use fgets to read the text, and then check it afterwards with isalpha or something similar.
Did you try to remove the '\n' from the string.
#include <ctype.h>
#include <stdio.h>
char buf[SIZE];
int good;
do {
size_t i;
char *pend;
good = 1;
fgets(buf, sizeof buf, stdin);
pend = strchr(buf, '\n');
if (pend != NULL)
*pend = '\0';
else
flush_stdin();
for (i = 0; buf[i] != '\0' && good; i++)
if (!isdigit(buf[i])
good = 0;
} while (good != 1);
How I fixed it:
// ----- Name Input ----- //
printf ("\nPlease enter Name:\n");
char nameCheck[50];
fgets (nameCheck,sizeof (nameCheck),stdin);
while (cCheck(nameCheck,50) == FALSE)
{
fgets(nameCheck,sizeof (nameCheck),stdin);
}
strcpy (c.name, nameCheck);
I added a new char nameCheck[50];
did an fgets() on it
did a while with the nameCheck and then did a strcpy :)