Getting a line of user input in C help. - c

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);

Related

Can I use getchar() to clear the buffer between scanf and fgets?

I have this function and it works well, but I was wondering if the usage of getchar is correct. Could I do something else instead? Thanks, guys.
void addRecord (struct record ** start, int account, char name[], char address[ ]);
{
printf("Enter your account number:\n");
scanf("%d", &account);
getchar();
printf("Enter your name:\n");
fgets(name, sizeof name, stdin);
printf("Enter your residential address:\n");
getaddress(address, sizeof address);
}
Assuming names cannot be empty and are not allowed to start with spaces (or tabs), you can read the name with another scanf while skipping over all intervening whitespace.
printf("Enter your account number:\n");
int account;
if(scanf("%d", &account) != 1) { /* error getting account */ }
printf("Enter your name:\n");
char name[80];
if(scanf(" %79[^\n]", name) != 1) { /* error getting name */ }
The breakdown of the " %79[^\n]" format specifier being used is:
the leading space skips over one or more whitespace characters (spaces, tabs, newlines), which consumes the newline left behind by the first scanf, any empty lines, and any leading spaces on the next line;
79 is a width specifier which limits scanf to reading at most 79 characters, leaving room for the terminating \0 nul character in the 80-character destination buffer;
[^\n] directs scanf to read a string up to (and not including) the \n newline character, which saves the name that the user entered into the name argument.
getchar() will only fetch a single character from stdin . To clear stdin buffer, you have use getchar() until you get EOF (end of file) or a new line. So, instead of calling getchar() once, call it a loop like this:
int c;
do {
c = getchar();
} while (c != EOF && c != '\n');
Note that the value of EOF is -1, so you must use int to handle it, as char is limited to 0 to 255 range.
Thanks to David Ranieri, the value of EOF can be (and usually) out of the range supported by char , so you should use int (or short ) to handle EOF.
You can put this code inside a function like clear_stdin if you need to reuse this code several times.

Character input being completely ignored in C

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.

Regarding structures in C

If I try to run this code then it doesn't ask me the value of s2.name. Why is it so?
#include<stdio.h>
int main()
{
struct student
{
char name;
int roll;
int age;
};
struct student s1;
struct student s2;
printf("Enter name of the student: ");
scanf("%c", &s1.name);
printf("%c", s1.name);
printf("\n");
printf("Enter name of the student: ");
scanf("%c", &s2.name);
printf("%c", s2.name);
return 0;
}
When you input a single character and press the Enter key, you are actually inputting two characters: The character in your input and a newline from the Enter key.
The second scanf reads this newline.
Or if you give multiple characters as input to the first name, then the second character will be read by the second scanf.
The way to solve the first problem is easy: Tell scanf to read and discard leading white-space (which newline is) by adding a single space in front of the format, like
scanf(" %c", &s2.name);
// ^
// Note space here
The way to solve the second problem is to read strings instead, which means you have to turn your name members into arrays and then use the "%s" format (preferably with a specified width so you don't read to many characters).
You are basically inputting two characters:
- the one that you type
- the newline character `\n` because you hit `enter`
A solution to this problem is clearing stdin after reading in the first "name":
#include<stdio.h>
int main()
{
struct student
{
char name;
int roll;
int age;
};
struct student s1;
struct student s2;
printf("Enter name of the student: ");
scanf("%c", &s1.name);
printf("%c", s1.name);
printf("\n");
fflush(stdin); //only works on windows, clears the input buffer
printf("Enter name of the student: ");
scanf("%c", &s2.name);
printf("%c", s2.name);
return 0;
}
Another way to clear the input buffer is:
while (getchar() != '\n');
This reads in all characters from the input buffer, because as soon as input is read by functions like getchar() or scanf(), the input is removed from stdin.
EDIT:
When you input values using getchar(), scanf(), etc., then the symbols that you type into stdin (most of the times via a keyboard) are stored in the input buffer at first (stdin). getchar() or any similar function then takes the values that it should read in, out of the input buffer. For example:
scanf("%d", &var);
scanf("%d", &var2);
If I input 5x, the characters in the input buffer are '5', 'x' and \n (because you hit the enter key). The first scanf() then takes out the '5', as it fits the format string %d. After that, the characters in the input buffer are 'x' and \n. In this case scanf returns 1, because it read in one value correctly.
When it then continues to the second one, scanf() won't even let you type anything, as there is already something in the input buffer stdin. It won't store any data however, because the first "item" in stdin ist 'x'. That doesn't fit the format string %d. The compiler then doesn't continue to read. In this case scanf would return 0, as no value was read in correctly.

storing and printing out elements of structs in C

I have created a simple struct:
struct album {
char title[20];
char artist[20];
};
and later in the program I created an instance a (struct album a;). I only need to ask the user to enter the title and the artist so that I save them in the appropriate place and then print the values:
printf("Please enter your album: \n");
printf("album name: ");
scanf("%.20s", a.title);
fflush(stdin);
printf("artist: ");
scanf("%.20s", a.artist);
printf("======\n");
printf("The album's name is %s and the artist is %s\n", a.title, a.artist);
however when I run this, I get this result:
Part of the input text is lost. What exactly goes wrong here?
scanf reads characters up to white space. So if you are entering Michel Jackson the space in between Michel and Jackson terminates your string so you only get the string Michel.
Try using something like fgets:
fgets(a.artist, 20, stdin);
fgets gets terminates the string on new line rather than white space.
**if you do use fgets make sure to remove the new line character from the end of the string of you do not want a new line character at the end.
With the %s specifier in scanf, the length specifier is the number of characters to write. This differs from most functions which expect the size of the entire buffer.
Since your buffer needs to contain the null terminator, you need to be using "%.19s" here. Another option is to not hardcode the value:
scanf("%.*s", (int)sizeof a.title - 1, a.title);
Note that this only works if a.title is an array (not a pointer), and the cast is necessary because int may be a smaller type than size_t but the * expects int.
As noted by others, %s is for reading up until the next whitespace. You can read up until the next line with %[ , for example:
scanf("%.19[^\n]", a.title);
The ^ means to keep reading until any of the following characters are hit, and \n actually means the newline specifically, not general whitespace.
You will want to flush the buffer after doing this, because the newline will still be in the buffer. However fflush(stdin); is not portable. The portable way to discard the rest of the current line is:
int ch; while( (ch = getchar()) != EOF && ch != '\n' ) { }

how to use fgets and sscanf for integers in loop

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.

Resources