How to implement gets() or fgets() while using structure pointer? - c

The below code runs perfectly with scanf(), but i want to input characters along with white space. I have tried gets() and fgets() (commented below), but its not working (its skipping to next iteration in the loop & displaying blank(NULL) during output for Name input used by fgets()). Any idea how to do it?
PS: I've tried fputs() with sample programs, its working fine. But i am little confused while using structure pointer.
#include <stdio.h>
#include <stdlib.h>
struct details {
uint Id;
char Name[20];
};
int main() {
int tot, i;
struct details *info;
printf("Enter no. of details to be stored = ");
scanf("%d", &tot);
info = (struct details *)malloc(tot*sizeof(struct details));
for(i=0; i<tot; i++) {
printf("%d)Id = ", i+1);
scanf("%d", &info->Id);
printf("%d)Name = ", i+1);
fgets(info->Name, 20, stdin); //How to make fgets() work??
//scanf("%s", &info->Name); //works fine with scanf()
info++;
}
for(i=0; i<tot; i++) {
--info;
}
printf("\nDisplaying details:\n");
for(i=0; i<tot; i++) {
printf("%d)Id = %d\n%d)Name = %s\n", i+1, info->Id, i+1, info->Name);
info++;
}
return 0;
}
output:
[xyz#xyz:Temp] $ ./fgets_Struct
Enter no. of details to be stored = 2
1)Id = 111
1)Name = 2)Id = 222
2)Name =
Displaying details:
1)Id = 111
1)Name =
2)Id = 222
2)Name =
[xyz#xyz:Temp] $

The problem comes from the "%d" scanf. It does not consume the line terminator, so the next read will interpret it as an empty line.
Your info allocation is wrong too. What you should use to allocate the size is not the size of info, but the size of an element pointed to by info.
printf("Enter no. of details to be stored = ");
scanf("%d\n", &tot); // <-- must consume end of line here
info = (struct details *)malloc(tot*
sizeof(*info)); // <-- use size of the pointed object, not the pointer
Also, tinkering with your info pointer is unnecessary and dangerous.
Something like that would be simpler and safer
for(i=0; i<tot; i++) {
printf("%d)Id = ", i+1);
scanf("%d\n", &info[i].Id)); // <-- store input in current info record
// and eat up the line terminator
printf("%d)Name = ", i+1);
fgets(info[i].Name, // <-- idem
sizeof(info[i].Name), // fgets will guarantee no buffer overflow
stdin);
}
printf("\nDisplaying details:\n");
for(i=0; i<tot; i++) {
printf("%d)Id = %d\n%d)Name = %s\n", i+1, info[i].Id, i+1, info[i].Name);
As for scanf / fgets, in your case (with "%s" format given to scanf), they should both read the input until a new line is entered, spaces included.
EDIT: I said something wrong here, sorry and thanks to chux for correcting me.
scanf ("%s") will indeed stop at the first white space. If you want the whole line, the easiest way is to use fgets. If you really want to use scanf, you'll need a syntax like "%[^\n]%*c".
To make sure your buffer does not overflow if the user types more than 20 characters, you can either
use "%19[^\n]%*c" as scanf format (20th character is needed for the '\0' terminator), or
use fgets with 20 passed as buffer size (fgets takes care of the terminator, it will read at most 19 characters to be able to add the '\0' without overflowing).
Of course you should use sizeof to compute max buffer size, like for instance:
fgets(info[i].Name, sizeof(info[i].Name), stdin);
Thus you won't have to modify the value if you decide, for instance, to have your buffer size changed to 50 characters.

First, you have a serious bug in your code.
info = (struct details *)malloc(tot*sizeof(info));
sizeof(info) returns either 4 or 8 (depending on the platform, 32 or 64 bit), but has nothing to do with your struct size. So you are allocating memory for pointers, but are using that space for your struct. Writing your data into this will overwrite other data in memory (although probably not in that simple example), because you allocate way too few bytes. What you want to use is sizeof(struct details) which would return 24.
Now to your input problem. Basically, scanf and gets should not be used together like that. The problem is that scanf reads what you type until you press return, which is not included. When you call gets after that, it sees this return and returns immediately with an empty string. If you change all your input calls to gets, it works perfectly. Example:
char test[256];
printf("Enter no. of details to be stored = ");
gets(test);
tot = atoi(test);

between the line scanf() for ID and fgets() put the line getchar()
like this
scanf("%d",&(info->Id));
getchar();

I had the same problem! Actually, I came to this page to get the solution and then I remembered the buffer!! So this is the way I solved it:
[...]
cout << "\nType the name: " ;
fflush(stdin); (you always have to clean the buffer before saving chars to a variable)
gets(pers.nom);
[...]
Cheers!!
P.S. cin and cout are from C++ but work like scanf and printf

Related

Why is the first value of my char array 10?

I'm new to programming but I wanted to make a program that gets as input a number, (length) and then stores a series of a's and b's of said length. Finally it should output the numbers as the ascii numbers. (so 97 and 98)
I thought I should malloc a char array of the size length and then do a for-loop over it and print everything as an integer.
The problem is however that I get a value 10 as the value of the first letter.
Thanks a lot for any help!
int main()
{
int length;
scanf("%d", &length);
char *matrix = malloc((length + 1 ) * sizeof(char));
for (int i = 0; i < length; i++)
{
scanf("%c", &matrix[i]);
}
for (int i = 0; i < length; i++)
{
printf("\n%d", matrix[i]);
}
return 0;
}
When inputting 3 on the first line and aba on the next line, I get 10 97 98.
However I expected it to be 97 98 97. Why do I get a value of 10 in the first place of the array?
Use
scanf(" %c", &matrix[i]);
^^^^
instead of
scanf("%c", &matrix[i]);
^^
When the format starts with a blank all white spaces are skipped.
From the C Standard (7.21.6.2 The fscanf function)
5 A directive composed of white-space character(s) is executed by
reading input up to the first non-white-space character (which remains
unread), or until no more characters can be read.
10 is the ASCII code of the (white space) new line character '\n' that was present in the input buffer after you entered the length of the array.
The first scanf() with the format string %d leaves a newline in the input buffer.
What happens here, is that your terminal collects input one full line at a time, passing it to the program, and then the scanf() only reads the digits from the buffer, leaving the newline character there for the next scanf() to see. The same would happen if you entered 10 abc: the space, abc and the newline would be left there.
This mismatch is not something people usually expect, and it's one of the things that makes scanf() annoying. I would suggest using fgets() instead to first read a full line, matching what the terminal gives, and then parse the number from it with sscanf() or strtol() (or atoi()).
This cleans up the issue at the point where the first line is read, instead of passing it on to the next input function to handle. Otherwise all your input functions are tied together, if the next input would be for a whole line with possible white space, you'd need to know if you expect to clear a pre-existing newline or not. (You could also replace the later scanf("%c") with getchar(), not that that matters with buffering though.)
That said, the scanf("%c")/getchar() loop may still see newlines if you enter lines that don't have as many characters as the loop expects, so if you don't want to see them at all, filter them out.
So, something like this:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int length;
char linebuf[100];
fgets(linebuf, 100, stdin);
length = strtol(linebuf, NULL, 10);
char *matrix = malloc(length + 1);
for (int i = 0; i < length; i++)
{
matrix[i] = getchar();
}
for (int i = 0; i < length; i++)
{
printf("\n%d", matrix[i]);
}
printf("\n");
return 0;
}
(The obvious downside of fgets() is that you have to decide on a maximum length for the input line, allocate a buffer and call another function in addition to it.)

Scanf won't continue after I seized an input text

so, I am basically building an array of students with their evaluation.
I made a basic struct :
struct Eleve{
char Nom[100];
int Note;
};
struct Eleve *array;
Then asking for how many children I want to input into my array, then loop and asking information for each child. But, when I enter an input into scanf("%s\n", array[i].Nom); the code stop.
int nbEleves;
float total = 0;
printf("Nombre?!\n");
scanf("%d", &nbEleves);
array = malloc(nbEleves * sizeof(struct Eleve));
int i = 0;
while (i < nbEleves) {
printf("Nom? ");
scanf("%s\n", array[i].Nom);
printf("La note? ");
scanf("%d\n", &array[i].Note);
total = total + array[i].Note;
i++;
}
It doesn't even go on the next printf. I don't understand why, because I don't get any build error or execution errors on this line. Eventually I would have looked at the format if by any chance I'd get an error there, but nothing. No errors, just not getting to the next step of the program.
I think my scanf looks right, and I've got no warnings on it. So, what can prevent the code to go further?
The data I entered in Nom is test.
You should not use scanf! This is an age old discussion that use of scanf requires care and is dangerous. Disadvantages of scanf.
Instead use fgets().
Check this answer How to read from stdin with fgets()? or this
Not able to input a string with spaces in a loop in C
But if you still insist on using scanf(), this may be useful:
while(i<nbEleves){
printf("Nom? ");
scanf(" %[^\n]",array[i].Nom);// remove the \n and notice the space before %[^\n]
printf("La note? ");
scanf("%d", &array[i].Note);
total = total + array[i].Note;
i++;
}
Also I suggest that you must free the memory you allocated using free() in order to prevent memory leaks.
free(array);
Remove the \n from scanf("%s\n", array[i].Nom); and scanf("%d\n", &array[i].Note); or press CTRL + D after you inserted the value.

Get a space in a multidimensional array

I am trying to get a name in an array " char name[100][100]". I tried doing many thing like these, but none work.Can you help me?
The code: Its a simple student's grade system i think, but only prints "" when trying to save a name.
#include <stdio.h>
#include <string.h>
void insert();
char name[100][100];float f[20];int z;
int main()
{
int x=0;
do{
printf("<1> Insert student\n");
printf("=> ");
scanf("%d",&x);
printf("\n*************************************************************\n");
switch(x){
case 1:
insert();
break;
default: printf("NO");
break;
}
}while(insert >=0 );
return 0;
}
void insert()
{
int x;
int y=0;
float n1,n2,p;
printf("How many students?: ");
scanf("%d",&y);
for(x=0;x<y;x++){
printf("Insert name: ");
fgets(name[x], 100, stdin);
int len = strlen(name[x]);
if (name[x][len-1] == '\n') {
name[x][len-1] = '\0';
}
printf("name[%d] = \"%s\"\n", x, name[x]);
printf("Insert first grade: ");
scanf("%f",&n1);
printf("Insert second grade: ");
scanf("%f",&n2);
printf("Insert final grade: ");
scanf("%f",&p);
f[x] = (n1 * 0.3)+(n2 * 0.3)+(p * 0.4);
z++;
}
for(x=0;x<z;x++){
if(f[x] < 6){
printf("the final grade of %s is: %.2f \n",name[x], f[x]);}
else{printf("the final grade de %s es: %.2f \n",name[x], f[x]);}
}
}
You should bear in mind that fgets() returns the new-line as well, if there's enough space in the buffer. You might want to take it out:
#include <stdio.h>
#include <string.h>
int main()
{
char name[100][100];
int y = 5;
int x = 0;
for (x = 0; x < y; x++) {
printf("Insert name: ");
fgets(name[x], 100, stdin);
int len = strlen(name[x]);
if (name[x][len-1] == '\n') {
name[x][len-1] = '\0';
}
printf("name[%d] = \"%s\"\n", x, name[x]);
}
}
Why are you using that %[^\t\n] string format? You should just go with a %s string format if you want to read a string (or, better, a %100s to limit the number of characters read).
scanf("%100s",name[x]) works just fine, but will mess things up when you try to use spaces (i.e. the scanf() will read one word at a time).
To avoid that, you can use the second option, that is fgets(). But, in this case, you need to pay attention to the final \n character that is appended to the string. To prevent the newline character from ending your string, you can simply do the following:
name[x][strlen(name[x])-1] = 0;
The previous code simply replaces the \n character with a null byte, thus ending the string and "ignoring" the newline.
EDIT:
The thing you need to understand is that the standard input (i.e. the keyboard input usually) is handled as if it were a file (in fact, you can use functions like fgets(), as if you were reading a normal file). So, as it happens with normal files, each line ends with a special character, \n. Every time you enter an input, and you press "Enter", a newline character is appended to your input.
There's a couple of things you need to know to understand what it is that you're doing wrong:
Some functions (like fgets()) read a line until a newline character is found. The newline character is also read, and returned in the string that was just read.
Other functions (like scanf()) also read lines until some special characters (such as or \n) are found. But, in this case, the final character is not read.
And, last: every time you open a file, the process keeps count of the number of characters you have read from the beginning of the file (or, to put it in an easier (and more correct) way, it "stores" a "pointer" to the next character that should be read).
With this being said, let's have a look at what happens with your program: first, the number of students is read (using scanf()), and, then, a name is read (using fgets()).
So, your input "file", looks like:
4\n
^
John Smith\n
...
The ^ is a pointer to the next character that should be read (and isn't, obviously, part of the input).
After the scanf() (which, as I mentioned, won't read the \n), the situation will be the following:
4\n
^
John Smith\n
...
Now, when you read the next line using fgets(), the "pointer" is already pointing to a newline character, and will therefore assume (correctly!) that the line has ended. The string you are reading is therefore "\n", instead of "John Smith\n".
The easiest way to fix this problem is to read, after every scanf(), single characters from standard input until a newline character is encountered.
scanf ( ... );
while (getc(stdin)!='\n');
Usually reading a single character should be enough, but in some cases (e.g. 4 \n) a single getc() isn't effective.
Basically, whenever a character is read from the file, the "pointer" is updated.
I really hope this cleared things up a bit. It isn't that easy to understand these details at first but, as you get more experience, things will definitely become clearer!

Scanning in strings into a 2D array in C

I need to take an input of 20 words entered by the user put those into a 2D array and print that out
my current code is
char array2[20][20];
int i;
for(i=0;i<20;i++)
{
printf("enter a word\n");
scanf(" %[^\n]",array2[i]);
}
for(i=0;i<colsize2;i++)
{
printf("\n");
for(j=0;j<rowsize2;j++)
{
printf("%c",array2[i][j]);
}
}
(I have no idea what %[^\n] is but it works better than %c or %s)
there are no compiler errors and the program will run but when it prints the array after all the words have been entered all I get is complete garbage
like
aȪ(M▒awn-US▒ e▒(<▒▒t/▒▒▒(5h▒tr:▒(
qh▒tdle__000
HW5.exe▒`wauld▒(▒&Oe,▒*a▒+a▒▒
so much so that it takes a bit of scrolling to get back to the start of my program
I do have more in this program that's not in my question but I'm 99% sure it wouldn't mess with what I have here but if you do want to see the rest just ask
I literally just started programming so I don't know diddly squat about it yet so if you could keep that in mind when you answer also this is for a school assignment so it doesn't need to be perfect it just has to get the job done
thanks to whoever answers this I've been grappling with this for hours
The format string
" %[^\n]"
^ note the leading space
means that scanf will first read and discard any number of leading whitespace characters and then match any sequence of characters which does contain a newline. scanf can potentially overrun the buffer it save the string into if the input string is too large for the buffer invoking undefined behaviour. The %s format specifier means scanf skips the leading whitespace characters and reads the input string till it encounters a whitespace at which point it appends a terminating null byte to the buffer it writes into and then returns.
Therefore, what you need is
char array2[20][20];
int i;
for(i = 0; i < 20; i++)
scanf("%19s", array2[i]);
for(i = 0; i < 20; i++)
printf("%s\n", array2[i]);
Initialize your array to 0:
char array2[20][20] = { 0 } ;
And then print the string not every character:
for(i=0;i<20;i++)
{
printf("%s",array2[i]);
printf("\n");
}

fgets() isn't prompting user a second time

Here is how the code's written.
int main()
{
char enteredName[30];
char stringNum[4];
char continueLetter = 0;
int continueProgram = 0;
int enteredAge;
int i;
do
{
memset(enteredName,'\0', 30);
printf("Please enter a name: ");
fgets(enteredName, 29, stdin);
printf("\n\nNow please enter your age: ");
fgets(stringNum, 3, stdin );
for(i = 0; i < 30; i++)
{
if (enteredName[i] == '\n')
{
enteredName[i] = '\0';
break;
}
}
for(i = 0; i < 4; i++)
{
if (stringNum[i] == '\n')
{
stringNum[i] = '\0';
break;
}
}
enteredAge = atol(stringNum);
} while();
When I run through the loop a second time, I'm not able to enter a new name into the char array, it just goes to the next prompt (the age). Unless this issue involves linked lists, the problem seems to be with something else. Could you help me find the error? Thanks!
Your second fgets call leaves characters (specifically the newline) waiting to be read from stdin if you enter a two digit age.
Increase the length parameter to match the array size:
fgets(stringNum, 4, stdin);
Or better:
fgets(stringNum, sizeof stringNum, stdin);
You probably want to do the same for enteredName.
From the fgets(3) man page:
The fgets() function reads at most one less than the number of characters
specified by size from the given stream and stores them in the string
str.
You don't need to reserve the extra array entry for the null-terminator like you're doing - fgets will handle that correctly on its own.
The problem is,you are not flushing the input buffer that is why the fgets() takes you directly to the second prompt asking age.This is common problem encountered,just add fflush(stdin);//my compiler supports itafter fgets();.Here is the code which has worked for me hope it works for you too :
EDIT: There is one very useful post providing information regarding fflush().As it is described that fflush is basically meant to be called to an output stream.Although some compilers provide support for flushing stdin,this is considered an undefined behavior.While having another look at the program, I found out that using sizeof can work wonders and is valid, So, I have modified the program for better. The use of sizeof is also described in one of the answers here.
#include<stdio.h>
#include<stdlib.h>
int main()
{
char enteredName[30];
char stringNum[4];
int continueProgram=0;
int i;
while(continueProgram<3)
{
setbuf(stdout,NULL);
printf("Please enter a name: ");
fgets(enteredName, sizeof enteredName, stdin);
printf("\n\nNow please enter your age: ");
fgets(stringNum,sizeof stringNum, stdin );
for(i = 0; i < 30; i++)
{
if (enteredName[i] == '\n')
{
enteredName[i] = '\0';
break;
}
}
for(i = 0; i < 4; i++)
{
if (stringNum[i] == '\n')
{
stringNum[i] = '\0';
break;
}
}
//enteredAge = atol(stringNum);
continueProgram++;
}
return 0;
}
The problem is that you don't know whether the string that has been read contains a newline or not. If it doesn't contain a newline, then this is going to be read by the next call to fgets, leaving an empty string in it. To prevent it, check if the line contains a newline character at the end. If not just read it using getchar() and Voila!!(Note that this solution is valid only to your problem, not in general). This code is to be added after reading the stringNum string.
if(stringNum[strlen(stringNum)-1]!='\n')
{
getchar();
}
This was happening because, if the age is a double digit, then fgets is going to read until the last digit and not the newline character. So,you need to read it in case the last char is not \n. If the age is a single digit, the the original program works fine.
You try this following piece of code:
if(stringNum[strlen(arr)-1]=='\n')
stringNum[strlen(arr)-1]='\0';
else
while(getchar()!='\n');
Whenever you enter a two digit age, the newline character which you insert while pressing enter gets stored in the buffer.
What this above piece of code is doing is that, it will check whether the last character of your storage is filled with a newline character, if yes, then it will replace it with the null terminator.
Else, it will keep reading from the buffer until and unless the newline character is removed from the buffer.
PS: If you are using borland then you will have fflush(stdin) to flush out any extra character from the buffer as indicated by PHlFounder, but if you happen to use gcc then this method is very good.
Also you can create a function or macro for this piece of code and call it every time you need, for eg.
void function(char * arr)
{
if(arr[strlen(arr)-1]=='\n')
arr[strlen(arr)-1]='\0';
else
while(getchar()!='\n')
}

Resources