Beginner with C here. I am trying to run a loop where strings and ints are entered into various fields of a struct. When prompted for a 'last name', the user can press enter with no other input and the loop should end.
The problem is that with this code, the loop doesnt end (last name and first name entry requests run together on the same line) and the value for salary always comes out wrong (0 or some large number)
while (employee_num <= 2)
{
printf("Enter last name ");
fgets(employee[employee_num].last_name, sizeof(employee[employee_num].last_name), stdin);
if(strlen(employee[employee_num].last_name) == 0)
break;
printf("Enter first name ");
fgets(employee[employee_num].first_name, sizeof(employee[employee_num].first_name), stdin);
printf("Enter title ");
fgets(employee[employee_num].title, sizeof(employee[employee_num].title), stdin);
printf("Enter salary ");
fgets(strng_buffer, 1, stdin);
sscanf(strng_buffer, "%d", &employee[employee_num].salary);
++employee_num;
getchar();
}
If I try this code instead, I am able to exit the loop properly after the first run through it, but cannot exit after that (by pressing enter at the last name portion - perhaps a \n I cant seem to clear?):
char strng_buffer[16];
while (employee_num <= 5)
{
printf("Enter last name ");
fgets(strng_buffer, sizeof(strng_buffer), stdin);
sscanf(strng_buffer, "%s", employee[employee_num].last_name);
if(strlen(employee[employee_num].last_name) == 0)
break;
printf("Enter first name ");
fgets(strng_buffer, sizeof(strng_buffer), stdin);
sscanf(strng_buffer, "%s", employee[employee_num].first_name);
printf("Enter title ");
fgets(strng_buffer, sizeof(strng_buffer), stdin);
sscanf(strng_buffer, "%s", employee[employee_num].title);
printf("Enter salary ");
scanf("%d", &employee[employee_num].salary);
++employee_num;
getchar();
}
I am curious as to how to make this work as intended and what best practice would be for entries like this (ie use of sscanf, fgets, etc)
Thanks in advance!
The Loop breaks prematurely when it encounters the break statement
if(strlen(strng_buffer) == 0)
break;
The uninitialized character buffer strng_buffer, coincidently has null as the first character causing strlen to return 0
I believe you may have intended
if(strlen(employee[employee_num].last_name) == 0)
break;
as the loop terminatorm, and it was a typo in your part causing premature loop exit.
Assuming the fix mentioned by Abhijit, why transform the first into the second? Are you aware that the second behaves differently to the first, because of the addition of sscanf? If your intention was to shorten the first, the second seems quite bulky. Rather than adding sscanf to the situation, why not shorten the first by declaring a struct employee *e = employee + employee_num; and using that repetitively, instead of employee[employee_num]?
One "best practise" regarding fgets is to check it's return value. What do you suppose fgets might return, if it encounters EOF? What do you suppose fgets would return if it's successful?
One "best practise" regarding scanf is to check it's return value. In regards to the return value of scanf, I suggest reading this scanf manual carefully and answering the following questions:
int x = scanf("%d", &employee[employee_num].salary); What do you suppose x will be if I enter "fubar\n" as input?
Where do you suppose the 'f' from "fubar\n" will go?
If it's ungetc'd back onto stdin, what would your next employee's last name be?
int x = scanf("%d", &employee[employee_num].salary); What do you suppose x will be if I run this code on Windows and press CTRL+Z to send EOF to stdin?
int x = scanf("%d %d", &y, &z); What would you expect x to be, presuming scanf successfully puts values into the two variables y and z?
P.S. EOF can be sent through stdin in Windows by CTRL+Z, and in Linux and friends by CTRL+D, in addition to using pipes and redirection to redirect input from other programs and files.
The problem is that fgets returns the string with the line break (\n) included. So, even the user presses return without entering info, the string won't be empty. Also, your buffer size for salary is too small.
So, either you strip out the \n on every fgets or you change your check to:
if(strlen(employee[employee_num].last_name) == 1) break;
Also, when you're getting the buffer, change 1 to something bigger, like
fgets(strng_buffer, 10, stdin);
However, if you do want to strip out the \n from each fgets, you can do something like:
employee[employee_num].last_name[strlen(employee[employee_num].last_name)-1] = 0;
You can do this for every string or, better yet, create a function that does it.
EDIT: if you can guarantee that the user will press enter after each input then you can safely assume this. However if it's not always the case it's possible that the last character is not \n and just stripping this way might cause problems.
Related
I am building a golf score board program and I need a single character input from the user asking if they are playing in a tournament. However when it comes to getting the single character input, it is completely ignored and the program quits. This is something that is tearing my hair out.
I've tried using different types of input like fgetc, getchar and scanf but all of these haven't worked.
void init(){
char tournamentOrNot;
char letter;
char name[40];
char golfClub[40];
char tournament[60];
printf("\nWhat is your name? ");
fgets(name, 100, stdin); //Input works
getchar();
printf("%s", name);
printf("Where are you playing golf at? NO SPACES\n");
fgets(name, 100, stdin); //Input works
getchar();
printf("Are you competing in a tournament?(Y/N)");
tournamentOrNot = getchar(); //Input ignored and program exits
if (tournamentOrNot == 'Y' || tournamentOrNot == 'y'){
printf("\nEnter the name for the tournament NO SPACES: ");
scanf("%s", &tournament);
printf("Initializing score board...");
printf("\n");
drawScoreBoard(name, golfClub, tournament);
}
else if (tournamentOrNot == 'N' || tournamentOrNot == 'n'){
tournament[7] = "Practice";
printf("Initializing score board...");
printf("\n");
drawScoreBoard(name, golfClub, tournament);
}
}
Current results:
What is your name? <input>
Where are you playing golf at?
<input>
Are you playing in a tournament or not? (Y/N)
<input ignored>
Program exited with code 0 Press any key to continue...
As you've seen, there are lots of ways of reading input in C, and it can be confusing to keep track of how they all work. And in particular, there are some crazy interactions that tend to happen if you mix different ways of reading input in the same program.
Most of the time, you'll get the best results if you use fgets to read everything. As you know, fgets reads lines of text. If you wanted something else -- a number, or a single character, or whatever -- the thing to do is to then process that line of text (that fgets just read for you) a bit further, to extract the information you want.
So with that said, here's your init function rewritten to use only fgets:
void init(){
char tournamentOrNot[10];
char name[40];
char golfClub[40];
char tournament[60];
printf("\nWhat is your name? "); fflush(stdout);
fgets(name, 40, stdin);
printf("Where are you playing golf at? "); fflush(stdout);
fgets(golfClub, 40, stdin);
printf("Are you competing in a tournament?(Y/N)"); fflush(stdout);
fgets(tournamentOrNot, 10, stdin);
if (tournamentOrNot[0] == 'Y' || tournamentOrNot[0] == 'y'){
printf("Enter the name for the tournament: "); fflush(stdout);
fgets(tournament, 60, stdin);
printf("Initializing score board...\n");
drawScoreBoard(name, golfClub, tournament);
}
else if (tournamentOrNot[0] == 'N' || tournamentOrNot[0] == 'n'){
strcpy(tournament, "Practice");
printf("Initializing score board...\n");
drawScoreBoard(name, golfClub, tournament);
}
}
The big change I made was to redeclare tournamentOrNot as an array of 10 characters, instead of a single char. So we're actually reading a one-line response to the question "Are you competing in a tournament?". In this case, extracting the information we're interested (the one-character response) is easy: the first character of the array is tournamentOrNot[0].
This seems strange at first: why treat tournamentOrNot as a string? You wanted a Y/N answer, so obviously you declared tournamentOrNot as a char, and tried to use getchar to read it. But as I said, it's tricky to mix things, and a mixture of fgets and getchar (and, further down, scanf) is very hard to get right. So if we want to read all our input using fscanf, we need tournamentOrNot to be a string also, even though we expect the user to type a one-character string like "Y" or "N". (But this way it will also work if the user disobeys the instructions, and types "Yes" or "no".)
I also made some other changes:
I added fflush(stdout) after the prompts, to make sure they come out. (Prompts that don't end in \n sometimes don't show up right away unless you flush the output.)
I got rid of the "NO SPACES" wording, because once we fix the inputs there shouldn't be problems with inputs with spaces in them.
I used fgets to read the tournament name, too, because the problems with mixing input styles get even worse if you throw scanf into the mix.
I changed tournament[7] = "Practice" to strcpy(tournament, "Practice").
I changed one instance of fgets(name, 100, stdin) to fgets(name, 40, stdin). (As #chux points out in a comment, eventually you'll want to learn a more reliable way of tracking these sizes, so you don't have to laboriously keep them matched up by hand.)
I changed the other instance of fgets(name, 100, stdin) to fgets(golfClub, 40, stdin); that may be behind your comment elsewhere that "the golf club question is being ignored".
Now, although I've just recommended using fgets to read all your input, there's one drawback to it that you're about to discover. It leaves the terminating \n character in the line of text it reads. Often you want to get rid of that. One way to strip it off, although it's mildly cumbersome, is to say things like
char *p;
fgets(name, 100, stdin);
if((p = strrchr(name, '\n')) != NULL) *p = '\0';
And there are plenty of other ways, with various tradeoffs between compactness and convenience and correctness.
Finally, if you're curious, here's some more explanation about the "crazy interactions that can happen if you mix different ways of reading input in the same program". The problem basically has to do with: newlines (\n) and other whitespace. Some input methods read the newline and give it back to you, some read the newline but don't give it back to you, some leave the newline on the input stream (which means that a later input call will find it). Some input methods skip other whitespace, some don't. (And in the case of scanf, some of its input directives skip whitespace and others don't.)
If you really want to, you can learn and keep track of all these seemingly-inconsequential differences, and write a program that uses a mixture fgets, getchar, and scanf, but actually works. Personally, though, I find this is far more trouble than it's worth, so I prefer to either use fgets (or some other line-reading function) everywhere, or use getchar everywhere. (And I never use scanf.)
There were several issues with the program function. I presume you do not want the newlines appearing in the strings, so they must be removed.
When using fgets the buffer size must be less or equal to the string size, otherwise you risk issues.
More in the commented program code below.
void init()
{
char tournamentOrNot;
char letter;
char name[100]; // <-- make name 100 char long
char golfClub[40];
char tournament[60];
printf("What is your name? ");
fgets(name, 100, stdin); //Input works <-- buffer same as name
// begin remove newline
int index = 0;
while (name[index] != '\n')
index++;
name[index] = '\0';
// end remove new line
// getchar(); <-- Not needed I think
printf("%s\n", name);
printf("Where are you playing golf at? NO SPACES\n");
fgets(name, 100, stdin); //Input works <-- buffer same as name
// begin remove newline
index = 0;
while (name[index] != '\n')
index++;
name[index] = '\0';
// end remove newline
// getchar(); <-- Not needed I think
printf("Are you competing in a tournament? (Y/N) ");
tournamentOrNot = getchar();
if (tournamentOrNot == 'Y' || tournamentOrNot == 'y')
{
printf("Enter the name for the tournament NO SPACES: ");
scanf("%s", tournament); // <-- tournament instead of &tournament
printf("Initializing score board...");
printf("\n");
//drawScoreBoard(name, golfClub, tournament); <-- does this function know the sizes of the strings ?
printf("socre board is drawn here\n");
}
else if (tournamentOrNot == 'N' || tournamentOrNot == 'n')
{
//tournament[7] = "Practice"; <-- that is wrong in C
strcpy(tournament, "Practice"); // <-- string copied with strcpy
printf("Initializing score board...");
printf("\n");
// drawScoreBoard(name, golfClub, tournament); <-- does this function know the sized of the strings ?
printf("socre board is drawn here\n");
}
}
There are three potential issues that I see:
fgets(name, 100, stdin);
If the line is longer than 100 characters, this will only read part of it. You should write a separate function that continues to read until it sees newline or EOF, reallocing the buffer as needed.
printf("Are you competing in a tournament?(Y/N)");
tournamentOrNot = getchar(); //Input ignored and program exits
The operating system doesn't necessarily send all input to the program as soon as a key is pressed. It's more efficient to buffer input and then send it in a single chunk, usually by waiting until it sees a newline (or EOF) and sending the whole line at once. Since this is operating system dependent, the fix will vary between systems. In Unix, the ncurses library is used to give more direct control of I/O, including disabling line buffering. I believe there's a clone of this library in Windows called pdcurses. Below is an example of a basic ncurses program that prints input characters as soon as the key is pressed. Further information can be found in the ncurses HOWTO.
#include <curses.h>
#include <stdio.h>
int main(void) {
int c;
initscr();//initialization
cbreak();//no line buffering
noecho();//when you type a character, it doesn't show up on screen
//getch is like getchar, but it reads immediately in cbreak mode
while ('q' != (c = getch())) {
//Note that we're explicitly sending both line feed
//and carriage return here; curses allows you to
//do one without the other.
printf("%c (%02x)\n\r", c, c);
}
endwin();//cleanup
}
Finally, an issue that isn't quite present in your program, but could become an issue if a few things were done differently: if an input function doesn't consume the newline character at the end of a line, it will still be in the buffer, and will be the first character read by the next input function. So, for instance, suppose the input is "42\nfoo\n", and you run the following code:
scanf("%d", &n);
fgets(buf, sizeof(buf), stdin);
scanf will read 42 into n, but not the '\n'. fgets will then read the newline, and immediately stop before getting to "foo", which is probably not what was intended. In this particular example, the best solution is generally to read the entire line, then use sscanf to parse it. But the general solution is just to always be aware of what is going to be left in the buffer after each statement.
This function will be called by the menu.
void exponentiation()
{
int i, result = 0, first, second;
printf("\n%s\n%s\n\n%s",
"1) Exponentiation",
"------------------",
"Enter 1st integer: ");
scanf("%d", &first);
printf("Enter 2nd integer: ");
scanf("%d", &second);
printf("%d raised to %d equals %d\n", first, second, result);
main();
}
From this function I need to read the user input, if the user input is "enter" without any integer, it should be going back to the menu which is calling the main().
I already tried to get the input.
For example:
if(first == '\n')
{main();}
or
if(first == 10) /**which is 10 is ASCII code for enter**/
{main()}
Both ways it didn't work at all, any suggestions?
both ways it didn't work at all, any suggestion
The function scanf returns the number of items it successfully scanned. You should check its return and go back if it doesn't matches your expectations.
Also you should know %d ignores whitespace. So if the user hits return without entering an integer, scanf simply skips over it and waits for something else.
If you insist on not ignoring whitespace this way, you should avoid scanf and use other input methods such as fgets. Get input from the user line by line and use sscanf, strtoul and strtok to make sense of it.
#include < stdio.h >
#include < process.h >
rec();
main() {
int a, fact;
char question, n, y;
do {
printf("\nEnter any number ");
scanf("%d", & a);
fact = rec(a);
printf("Factorial value = %d\n", fact);
printf("do you want to exit.....(y/n):");
scanf("%s", & question);
}
while (question == n);
exit(0);
}
rec(int x) {
int f;
if (x == 1) return 1;
else f = x * rec(x - 1);
return f;
}
In this program I want to get factorial of the entered number, which I get. But I also want the user to say whether to exit or get the factorial of another number, which I can't do. It asks user but when I enter "n" it exits.
Where is the error?
You want
while (question == 'n');
Or
char question, n = 'n', y = 'y';
Though I find the 2nd version a little redundant.
Either way you need to change
scanf("%s"
to
scanf("%c"
To correctly read in a single char and not a string. Thanks RageD
One problem is the combination of:
char question, n, y;
scanf("%s", &question);
You are using %s to read a null-terminated string into a single character. Even if you hit 'y' and return, you'll be overwriting beyond the end of the variable. This is not good. (The good news is that "%s" skips over white space, including the newline after the number).
You either need to use "%c" in the format:
char question;
scanf(" %c", &question); // NB: The leading space is important!
or you need to use a string format and a string variable (and no &):
char question[10];
scanf("%9s", question);
If you use an array, you need to consider whether to use strcmp(), or whether to compare the first character from the input:
while (strcmp(question, "n") == 0);
while (question[0] == 'n');
You probably got told by the compiler that you'd not declared variable n so you added it. You probably need the loop to end with while (question == 'n');and then get rid of the (now) unused variablen(and the currently unused variabley`).
Note that if you use omit the space in the " %c" format string:
scanf("%c", &question);
then it will normally get the newline after the number, which won't be 'n', so your loop will exit every time, apparently without waiting for you to enter anything. You can finesse that with scanf(" %c", &question); which skips white space before reading a character.
You should test that scanf() received the input you expected each time you use it. The correct test for single item inputs is:
if (scanf(" %c", &question) != 1)
...input failed...
If you need to distinguish between EOF and conversion failure, you can capture the return from scanf():
int rc;
if ((rc = scanf(" %c", &question)) != 1)
...rc == EOF on EOF; rc == 0 on 'conversion failure'...
...a single character input can't easily fail...
...but if someone types 'a' instead of '9' when you're looking for a number...
Getting I/O right using scanf() is distressingly hard. Many experienced programmers simply don't use it; it is too hard to get right. Instead, we use fgets() or POSIX getline() to read a line of data, and then use sscanf() to parse it. There are many advantages to this, but a primary one is that the newline has been eaten so you don't run into problems with the variable question not containing the answer you expect.
I am new to C and working through some exercises, but having trouble with gets() in a while loop. In searching, I believe that it may have something to do with the \n character, but I was hoping that someone would be able to give me a more thorough explanation of what is going on here:
This loop will only run once - it will print the 'Enter last name' to screen a second time and then drop out of the loop before gets() has a chance to take any input a second time:
while (employee_num <= 10)
{
printf("Enter last name ");
gets(employee[employee_num].last_name);
if(strlen(employee[employee_num].last_name) == 0)
break;
printf("Enter first name ");
gets(employee[employee_num].first_name);
printf("Enter title ");
gets(employee[employee_num].title);
printf("Enter salary ");
scanf("%d", &employee[employee_num].salary);
++employee_num;
}
Thanks in advance!
You'd have a newline character (\n) in the input buffer after reading the salary. That is being picked up as the last name in the second iteration. You can ignore that by adding a getchar() after your last scanf:
while (employee_num <= 10) {
...
printf("Enter salary ");
scanf("%d", &employee[employee_num].salary);
++employee_num;
getchar();
}
Referring to answer by skjaidev,
With gets(), The newline character(\n), if found, is not copied into string and this is the reason for your problem.
Also,
Notice that gets is quite different from fgets: not only gets uses stdin as source, but it does not include the ending newline character in the resulting string and does not allow to specify a maximum size for str (which can lead to buffer overflows).
It is considered to be a bad practise to use gets() in a program
The following is the code for a simple menu. If the user enters an invalid option (4 or 'o') the menu should be reprinted displaying an error message. The code works when the user inputs a number, however it fails (looping indefinitely) when the user inputs a letter or string into option. My guess is that memory is being overwritten in the looping cases generating the strange behavior. Could someone help me to fix it?
Thank You
int inmenu = 1;
while (inmenu){
//Menu
printf("User: %s %s\n", user.firstname, user.lastname);
printf("0) Exit\n1) List Friends\n2) Add Friend\n3) Delete Friend\nWhat would you like to do? ");
int option;
scanf("%i", &option);
if(option == 0)
inmenu = 0;
else if (option == 1)
defaultPrint(friends, numfriends, NULL);
else if (option == 2){
//addfriend
char *name = (char *) malloc(sizeof(char) * 256);
int birthdate;
printf("Enter first name: ");
scanf("%s", name);
printf("Enter last name: ");
scanf("%s", name);
printf("Enter birthdate: ");
scanf("%i", &birthdate);
}
else if (option == 3){
//deletefriend
defaultPrint(friends, numfriends, NULL);
int n;
printf("What friend would you like to delete? ");
scanf("%i", &n);
}
else
printf("ERROR: Invalid option %i\n", option);
}
Test input:
0) Exit
1) List Friends
2) Add Friend
3) Delete Friend
What would you like to do? 4
ERROR: Invalid option 4
0) Exit
1) List Friends
2) Add Friend
3) Delete Friend
What would you like to do?
(correct behavior)
0) Exit
1) List Friends
2) Add Friend
3) Delete Friend
What would you like to do? o
What would you like to do? ERROR: Invalid option 4
0) Exit
1) List Friends
2) Add Friend
3) Delete Friend
...
(incorrect behavior keeps printing the same last 5 lines in an infinite loop)
Why use scanf? How about use getchar, check the range, see if it's 0-4, and if not, error message.
The cause of your problem is that scanf() doesn't consume the input stream if it doesn't match the requested format.
This means that when you call:
scanf("%i", &option);
If the user enters something other than a number (like "o"), then that user input remains in the input stream - so when you loop around and call scanf() again, the "o" is still there, and still doesn't match the requested format.
This is why scanf() is not particularly suitable for taking user input - it's designed for consuming well-formatted data files. Instead, since you have a line-oriented user interface, you should read an entire line from the user with fgets(), then try parsing it with sscanf(). If it doesn't parse correctly, you can report an error and then read a new line from the user.
When you enter a number and press enter, scanf reads the number but leaves the newline in the buffer, when the loop loops, scanf will read the newline from the buffer, see that it doesn't match the format, and leave it there. This will repeat forever.
A quick fix could be to add the newline to the format:
scanf("%i\n", &number);
but this will fail if the user doesn't enter exactly the data you expect, newline isn't the only thing that will cause the behaviour, anything that doesn't match the format is left in the buffer.