3

I'm trying to run the following code in the basic ubuntu gcc compiler for a basic C class.

#include<stdio.h>

struct emp
{

  int emp_num, basic;
  char name[20], department[20];

};

struct emp read()
{
  struct emp dat;

  printf("\n Enter Name : \n");
  scanf("%s", dat.name);

  printf("Enter Employee no.");
  scanf("%d", &dat.emp_num);

  //printf("Enter department:");
  //fgets(dat->department,20,stdin);

  printf("Enter basic :");
  scanf("%d", &dat.basic);

  return dat;
}

void print(struct emp dat)
{
  printf("\n Name : %s", dat.name);

  printf("\nEmployee no. : %d", dat.emp_num);

  //printf("Department: %s", dat.department);

  printf("\nBasic : %d\n", dat.basic);
}

int main()
{
  struct emp list[10];

  for (int i = 0; i < 3; i++)
  {
    printf("Enter Employee data\n %d :\n", i + 1);
    list[i] = read();
  }

  printf("\n The data entered is as:\n");

  for (int i = 0; i < 3; i++)
  {
    print(list[i]);
  }

  return 0;
}

I want the name to accept spaces.

The problem comes when I'm entering the values to the structures. I am able to enter the name the first time but the subsequent iterations don't even prompt me for an input.

I've tried using fgets, scanf("%[^\n]",dat.name) and even gets() (I was desperate) but am the facing the same problem every time.

The output for the 1st struct is fine but for the rest is either garbage, the person's last name or just blank.

Any ideas?

alk
  • 69,737
  • 10
  • 105
  • 255
Suman Roy
  • 673
  • 5
  • 18
  • 1
    If you **really need** to "scan" "strings" from the **user** use `%Ns` with `N` being replaced by an integer describing the size of the buffer to read into. In the OP's example this would be `%19s` leaving room for the necessary `0`-termination of the "string". Doing so avoids potential buffer overflows. – alk Sep 01 '13 at 10:27
  • But scanf doesn't read spaces and I want people to be able to enter their full name – Suman Roy Sep 01 '13 at 14:54
  • So you might switch to `fgets()` or even `read()` and do the parsing of what had been entered on your own. – alk Sep 01 '13 at 17:39

3 Answers3

3

When reading a string using scanf("%s"), you're reading up to the first white space character. This way, your strings cannot include spaces. You can use fgetsinstead, which reads up to the first newline character.

Also, for flushing the input buffer, you may want to use e.g. scanf("%d\n") instead of just scanf("%d"). Otherwise, a subsequent fgets will take the newline character and not ask you for input.

I suggest that you experiment with a tiny program that reads first one integer number and then a string. You'll see what I mean and it will be much easier to debug. If you have trouble with that, I suggest that you post a new question.

nickie
  • 5,608
  • 2
  • 23
  • 37
  • could you be a little more specific because I think my problem has something to do with the buffer. and like I said I have already tried fgets() but with the same result – Suman Roy Sep 01 '13 at 10:44
  • You need to flush the buffer in between reading an integer number and a string, if you expect the two to be given in two different lines. Do this by `scanf("%d\n", &n);`. As I think you don't understand well enough how the input buffer works, try experimenting with a small program first. – nickie Sep 01 '13 at 10:49
  • But if I do that I'll have to enter an extra value between the actual values I need. And if do put another `scanf()` before `printf("Enter Employee no.");` I get get promted for data at all the wrong points. – Suman Roy Sep 01 '13 at 14:52
  • 1
    Look, I hate doing other people's exercises. This is not necessarily your case, but I feel it is. I think that the point in all this is to be guided to the right direction, not to be given the solution to a very simple problem in your hand. – nickie Sep 01 '13 at 15:03
  • @user2737039 Also try another call to `fgets()` and then `strtol()` for getting an integer instead of messing around with `scanf()`. The `scanf()` function is so counterintuitive and certainly not trivial to use that all beginners better stay far away from it. –  Sep 01 '13 at 20:18
3

The problem is that scanf("%[^\n",.. and fgets don't skip over any whitespace that may be left over from the previous line read. In particular, they won't skip the newline at the end of the last line, so if that newline is still in the input buffer (which it will be when the last line was read with scanf("%d",..), the scanf will fail without reading anything (leaving random garbage in the name array), while the fgets will just read the newline.

The easiest fix is to add an explicit space in the scanf to skip whitespace:

printf("\n Enter Name : \n");
scanf(" %19[^\n]", dat.name);

This will also skip over any whitespace at the beginning of the line (and blank lines), so may be a problem if you want to have a name that begins with a space.

Note I also added a length limit of 19 to avoid overflowing the name array -- if the user enters a longer name, the rest of it will be left on the input and be read as the employeee number. You might want to skip over the rest of the line:

scanf("%*[^\n]");

This will read any non-newline characters left on the input and throw them away. You can combine this with the prior scanf, giving you code that looks like:

printf("\n Enter Name : ");
scanf(" %19[^\n]%*[^\n]", dat.name);
printf("Enter Employee no. : ");
scanf("%d%*[^\n]", &dat.emp_num);
printf("Enter department : ");
scanf(" %19[^\n]%*[^\n]", dat.department);
printf("Enter basic : ");
scanf("%d%*[^\n]", &dat.basic);

This will ignore any spurious extra stuff that someone enters on a line, but will still have problems with someone entering letters where numbers are expected, or end-of-file conditions. To deal with those, you need to be checking the return value of scanf.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • sorry, I tried it and still having the same problem; I still dont get the prompt for the next name. – Suman Roy Sep 02 '13 at 11:33
  • @user2737039: take the code you posted, change the `"%s"` in the `scanf` to `" %19[^\n]"`, and it works fine, as long as you don't enter a too-long name. Don't forget the space -- it is very important. – Chris Dodd Sep 02 '13 at 17:24
  • Not sure why you have `%*[^\n]` in some of the `scanf`s. They fail because the `%[^\n]` format specifier will fail if the first character to be read is a `\n`. – Spikatrix Feb 21 '17 at 12:10
  • @CoolGuy: Those are to consume and discard everything up to the next newline. If there is nothing, they will do nothing, but if there is random garbage left on the line (if the string entered was too long, or there was cruft after the number), it will be discarded. – Chris Dodd Feb 23 '17 at 05:55
  • Ah, yes. You're right. Forgot about the case when input is too long. My bad. – Spikatrix Feb 23 '17 at 06:30
0

What you have tried was:-

scanf("%[^\n]",dat.name)

In this you forgot to specify the specifier.

You can try to use this:-

scanf ("%[^\n]%*c", dat.name);

or fgets() if you want to read with spaces.

Note:- "%s" will read the input until whitespace is reached.

Rahul Tripathi
  • 168,305
  • 31
  • 280
  • 331
  • actually i tried it both way with the same result – Suman Roy Sep 01 '13 at 10:40
  • Did you try to use this:- scanf("%[^\n]s",dat.name); ?? as it keeps taking in values until it encounters a '\n' – Rahul Tripathi Sep 01 '13 at 10:44
  • I did that too. It'll read the spaces. but i dont get a promt to enter the next string – Suman Roy Sep 01 '13 at 14:42
  • -1: the `[` is the specifier; there's no need for another specifier after it (unless you want to read something else as well.) – Chris Dodd Sep 01 '13 at 20:18
  • @ChrisDodd:- So what you mean is scanf ("%[^\n]%*c", dat.name); is equivalent to scanf("%[^\n]",dat.name); ??? Please correct me if I am missing something!! – Rahul Tripathi Sep 01 '13 at 20:20
  • @RahulTripathi: The two are equivalent in that they'll read the exact same thing into `dat.name`. The only difference is that the first one will then read one more character and throw it away (for the `%*c` pattern) – Chris Dodd Sep 01 '13 at 20:30