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.
Related
So I want to loop through infinitely and take a user string as input and then sort it. I want to continuously take input until the user enters 'EXIT'. That part of the code works. However, It won't loop infinitely properly. (This is also in strict C).
It should ask the user to input a string, sort it, print it, ask the user to input a string, sort it, etc. etc. However, what it does, is it asks the user to input a string, sorts it, prints it, then it prints it, prints it, prints it, etc. What is wrong here? Thanks.
int main(){
//Allocate a 100 character long array in the heap which will be passed and altered in place and outputted.
//I'm not fully sure how to allocate dynamically the appropriate amount of space for strings without creating a whole class for it.
//But seeing as this is c and not c++ then I'm not sure. Cause I need to have a length of how long the input is, but I need the memory allocated
//to store the string ti find the length of.
char* input = (char*)malloc(100);
//Scanf allows input for any characters including white space.
while(1){
printf("Enter an input string: ");
scanf("%[^\n]", input);
int len = strlen(input);
//Checks for exit condition. Input string being 'EXIT'
if(len == 4 && input[0] == 'E' && input[1] == 'X' && input[2] == 'I' && input[3] == 'T'){
break;
}
else{
//Function calls
remove_duplicates(input);
sort(input);
printf("\"%s\"\n", input);
}
}
free(input);
input = NULL;
return 0;
}
Many format specifiers for scanf skip leading while-space (like newlines), but not "%[".
So the newline that is added by you pressing Enter in the first input will be the the first thing that the next call to scanf will read. And with your format a newline (or really, any space) will tell scanf to stop reading, which means nothing more will ever be read. Over and over again.
I would rather recommend you use fgets to read lines, as that also included automatic buffer-overflow protection (granted you pass the correct buffer size).
And if you're worried about the newline added by fgets it's easy to remove with the strcspn function.
So better do something like
char input[100];
while (fgets(input, sizeof input, stdin) != NULL)
{
input[strcspn(input, "\n")] = 0; // Remove possible trailing newline
if (strcmp(input, "EXIT") == 0)
break; // Exit
else
{
// Rest of code
}
}
I am trying to get a single digit number from stdin.
Using scanf("%d",&choice); is not good because if something like 3fjios or fjaifdj is entered then it keeps everything after the digit (if there is one), so if later I have scanf("%s",name); it takes the other chars and messing up. And also using scanf is bad (or so it seems from Google).
After a lot of digging I understand that we should use fgets, to read input into a string and then parse through it.
But! Nowhere is explained how to properly clear the buffer afterwards.
So if I do something like:
char choice[3];
do {
fgets(choice, 3, stdin);
scanf("%*[^\n]");
scanf("%*c");//clear upto newline
} while (choice[1] != '\n');
this works only if I enter a string longer than 2 chars.
When I enter a single char for fgets then the scanf actually waits for another input... which is bad.
The other big problem is if I enter more than 2 chars (a digit and '\n') then the first 2 chars go into choice, the rest are stuck in the buffer. All the approaches to clearing it seems like they require one to build a nuclear power plant first...
Also, what happens if the user enters an infinitely (a really long) long string?
Can you please show a simple way that will allow the user to enter some string of some (unknown) length, and then to properly check if it contains exactly a single digit at the start, followed by '\n'?
Any other input should loop back to get a new input from the user again.
please don't use complex solutions, only standard simple C please.
I can't believe I wasted 6 hours on this supposedly simple technical thing, just getting an input from the user. Solving the actual problem was easier...
Do not use scanf. It is making things overly complicated. Just use getchar to read and discard the line. eg:
int read_input(void) {
int n;
n = getchar();
if( getchar() == '\n' || n == EOF)
return n;
else
do n = getchar(); while ( n != '\n' && n != EOF);
fputs("invalid entry: ", stderr);
return read_input();
}
int main(void) {
int input;
input = read_input();
printf("user entered: %c\n", input);
return EXIT_SUCCESS;
}
I'm newcomer to C and I am stuck. I want to write simple program, which will take input from keyboard and output it if it isn't an 'exit' word. I've tried few different approaches and none of them works. Almost in all cases I get infinite output of the first input.
Here is one of my approaches:
#include <stdio.h>
int main() {
char word[80];
while (1) {
puts("Enter a string: ");
scanf("%79[^\n]", word);
if (word == "exit")
break;
printf("You have typed %s", word);
}
return 0;
}
I thought after it finish every loop it should give me prompt again, but it doesn't.
What I am doing wrong.
Please if you know give me some advice.
Thanks in advance. Really, guys I will be so happy if you help me to understand what I am doing wrong.
Oh, by the way I've noticed that when I typed some word and press 'Enter', the result string also include Enter at the end. How can I get rid of this ?
Improper string compare - use strcmp().
if (word == "exit") simply compares 2 address: the address of the first char in word and the address of the first char in string literal "exit". Code needs to compare the content beginning at those addresses: strcmp() does that.
Left-over '\n' from the previous line's Enter. Add a space to scanf() format to consume optional leading white-space. Also check scanf() results.
scanf() specifiers like "%d", "%u" and "%f" by themselves consume optional leading white-space. 3 exceptions: "%c", "%n" and "%[".
Add '\n' at end of printf() format. # Matt McNabb
#include <stdio.h>
int main() {
char word[80];
while (1) {
puts("Enter a string: ");
// v space added here
if (scanf(" %79[^\n]", word) != 1)
break; // Nothing saved into word or EOF or I/O Error
if (strcmp(word, "exit") == 0)
break;
printf("You have typed %s\n", word);
}
return 0;
}
Nice that OP used a proper width limited value of 79 in scanf()
Oh, by the way I've noticed that when I typed some word and press 'Enter', the result string also include Enter at the end. How can I get rid of this ?
This is because you don't output a newline after printf("You have typed %s", word);. The next statement executed is puts("Enter a string: "); . So you will see You have typed helloEnter a string:. To fix this, change to printf("You have typed %s\n", word);
As others have mentioned, use strcmp to compare strings in C.
Finally, the scanf format string "%79[^\n]" does not match a newline. So the input stream still contains a newline. Next time you reach this statement the newline is still in the stream , and it still doesn't match because you specifically excluded newlines.
You will need to discard that newline (and any other input on the line) before getting the next line. One way to do that is to change the input to scanf("%79[^\n]%*[^\n]", word); getchar(); That means:
Read up to 79 non-newlines
Read all the non-newline things , and don't store them
Read a character (which must be a newline now) and don't store it
Finally it would be a good idea to check the return value of scanf so that if there is an error then you can exit your program instead of going into an infinite loop.
The specifier [^\n] will abort scanf if the next character is a newline (\n), without reading the newline. Because of that, the scanf calls after the first one won't read any input.
If you want to read single words, use the %79s specifier and the following code to remove the \n at the end of your string:
if(word[strlen(word)]=='\n')
word[strlen(word)]='\0';
If you want to read whole lines, you can remove the newline from the input buffer this way:
char line[80];
int i;
while(1)
{
puts("Enter a string:");
i=-1;
scanf("%79[^\n]%n",line,&i);
//%n returns the number of characters read so far by the scanf call
//if scanf encounters a newline, it will abort and won't modify i
if(i==-1)
getchar(); //removes the newline from the input buffer
if(strcmp(line,"exit")==0)
break;
printf("You have typed %s\n",line);
}
return 0;
It is better to clear (to have a reproducible content) with memset(3) the memory buffer before reading it, and you should use strcmp(3) to compare strings. Also, consider using fflush(3) before input (even if it is not actually necessary in your case), don't forget to test result of scanf(3), also most printf(3) format control strings should end with a \n -for end-of-line with flushing- so:
#include <stdio.h>
int main() {
char word[80];
while(1) {
puts("Enter a string: ");
memset (word, 0, sizeof(word)); // not strictly necessary
fflush(stdout); // not strictly necessary
if (scanf("%79[^\n]", word)<=0) exit(EXIT_FAILURE);
if (!strcmp(word,"exit"))
break;
printf("You have typed %s\n", word);
};
return 0;
}
I would suggest reading a whole line with fgets(3) and getting rid of its ending newline (using strchr(3)). Also read about getline(3)
Don't forget to compile with all warnings and debug info (e.g. gcc -Wall -g) and learn how to use the debugger (e.g. gdb)
Your first problem is that you can't compare a string with '=='. So:
if (word == "exit")
should be
if ( strncmp( word, "exit", 4 ) == 0 )
(You could also use strncmp( word, "exit", strlen(word) ) if you know that word is zero-terminated and safe from bad values. There's a few other options also.)
Your second problem is that scanf() is not consuming the input, probably because it's not matching what you've told it to expect. Here is a good explanation of how to do what you want to do:
http://home.datacomm.ch/t_wolf/tw/c/getting_input.html
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.
I've been all over the web and tried a bunch of different things, but they never seem to work. Every time I run the program it skips over the chance to enter something for the students name and goes straight to the department. Also, we are new to C and were really told to use printf and scanf, but when the user puts in a name like, joe shmo, it does some weird stuff.
fputs("Please enter the students name: ", stdout);
fflush(stdout);
fgets(studentArray[empty].name, sizeof studentArray[empty].name, stdin);
printf("\nPlease enter the students department: ");
scanf("%s", studentArray[empty].department);
printf("\nPlease enter the students rank: ");
scanf("%d", &studentArray[empty].rank);
EDIT: Weird stuff as in, if I enter two names, ie joe shmo, it will take joe as the name and automatically add shmo to the department. studentArray is an array of a struct I made...
typedef struct {
char name[MAX_NAME_LENGTH];
char department[MAX_DEPT_LENGTH];
int rank;
} student;
When the program reaches the fgets() I bet there is a pending '\n' in the input buffer from a previous scanf(). I suggest you get rid of that '\n' and any previous input.
For example, with
int getridofextrainput(void) {
int ch;
while (((ch = getchar()) != '\n') && (ch != EOF)) /* void */;
return ch;
}
Then use that function in your code where you think it's necessary (before fgets).
This is what happens when you mix fscanf and fgets.
When fscanf reads a number, it stops right at the end of this number, before the following newline character. When afterwards fgets reads a string, it stops at the next newline character, which unfortunately immediately follows.
If you used only fscanf to read all data, you would not get problems. This is not trivial, because some of your names contain spaces. It is possible to read a name containing a space using fscanf like this:
scanf("%[^\n]", studentArray[empty].department);
If you used only fgets to read all data, you would be OK either. Unfortunately, this is not trivial too: it requires a temporary buffer for reading numbers.
char temp[42];
fgets(temp, sizeof temp, stdin);
sscanf(temp, "%d", &studentArray[empty].rank);