ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
ok well my problem is scanf seems not to like spaces.. where it asks for your favorite food it craps out when you put somthing like rice and beans but if you put somthing just like carrots it handles it fine .. any suggestions? and and i forgot to mention im just learning .. like it matters
Code:
#include <stdio.h>
int
main()
{
int age; // sets age as an integer
age = 16; // age can be changed to anything you want
char name[8] = "anthony"; // name can be changed to what ever you want
char food[20];// no need to touch
char color[20];// no need to touch
int born; // sets born as an integer
printf("hi my name is %s and my age is %d\n",name, age);
printf("o wait how about some more info?\n");
printf("Whats your favorite food?\n");
scanf("%s",food);
printf("Whats your favorite color?\n");
scanf("%s",color);
printf("What year where you born?\n");
scanf("%d",&born);
printf("\n\nOk lets get this straight, your name is %s",name);
printf("you are currently %d years old\n",age);
printf("your favorite food is %s\n",food);
printf("your favorite corlor is %s\n",color);
printf("and last but not least you where born in the year %d\n",born);
return 0;
}
fgets() adds the \n (from when the user presses ENTER) to the buffer you pass to it (food in this case) so you have to chop it off with something like that third line of code.
Scanf() interprets spaces as seperators, and tries to write the text after a space to the next variable. Since you don't provide a next variable, scanf() start writing wildly into memory that belongs to other variables or even even beyond that, possibly resulting in a "segmentation fault" crash of your program.
With scanf, this can not really be solved, because you never know for sure how many words (texts seperated by one or more spaces) the user will try to type.
"%[^\n]" takes everything up to a newline char and assigns it to 'food' which must be a char *. I often use this to take from this point to the 'end of the line'.
Wheter its used in scanf to get string from cmdline, or in sscanf to parse a string returned from fgets; it takes everything including spaces EXPECT the newline. So you won't need to cleanup the string later with strlen.
You might not need to go through the trouble of removing the \n from the end of the string, but you're introducing a gigantic security hole. scanf() has no way of knowing how big food is, so it will just copy everything the user types into it, even if it goes past the end of the array.
You might not care for little class projects or whatever, but it's better to start using good habits early.
Originally posted by itsme86
You might not care for little class projects or whatever, but it's better to start using good habits early.
I do care, and you are right about developing good habits now. However, knowing about the buffer overrun problems associated with scanf use allows you to use it safely and in the correct fashion. You had already said it, so I didn't say it again...
The above example I think would resolve any concerns about buffer overruns, etc. From a logic standpoint, you may want to consider a different combination of statements - but thats what programming is all about...
fgets(food, sizeof(food), stdin);
if(food[strlen(food)-1] == '\n')
food[strlen(food)-1] = '\0'; <--Why do you have to terminate the string i thought fgets does this for you
Code:
char ch_food[521];
char ch_buffer[512];
...
fgets ( ch_buffer, sizeof(ch_buffer), fp );
...
sscanf (ch_buffer, "%[^\n]", ch_food ); <-- This would be redudent
...
Originally posted by exvor Hmm confuseing code here
Code:
fgets(food, sizeof(food), stdin);
if(food[strlen(food)-1] == '\n')
food[strlen(food)-1] = '\0'; <--Why do you have to terminate the string i thought fgets does this for you
from fgets man page:
fgets() reads in at most one less than size characters from stream and
stores them into the buffer pointed to by s. Reading stops after an EOF
or a newline. If a newline is read, it is stored into the buffer. A
'\0' is stored after the last character in the buffer.
SO the above code is correct
Also another problem that may occur:
if you prompt for food, and the user types more charecters than the buffer size and then hits the enter:
lets say the buffer can store 6 chars: 5 chars and '\0'. If you type 8 chars and then '\n' (new line), the next time you do fgets (lets say for color) the function will get as input the remaining 3chars and the new line. So If you use this approach , you need to check if the buffer is filled, without getting a new line, and if this is the case, u nead to manually get the remaining chars till the new line(e.x. getchar() with a for loop) and ignore them, so you don't get them the next time you prompt for something else
Last edited by perfect_circle; 02-27-2005 at 10:52 AM.
Why do you have to terminate the string i thought fgets does this for you
I was just getting rid of the \n at the end.
Quote:
sscanf (ch_buffer, "%[^\n]", ch_food ); <-- This would be redudent
skoona is doing the same thing.
@skoona: Your method will work as well. I was specifically talking about scanf() in my statement, but you used sscanf(). The difference is that scanf() reads from an untrusted source (the user), and sscanf() works from a trusted source (a buffer that you've created).
@perfect_circle: No, the correct code is the one I originally posted. You only want to subtract 1 from strlen(). If the user enters "hello" then fgets() will store this in the buffer: 'h' 'e' 'l' 'l' 'o' '\n' '\0' (assuming the buffer is sufficiently big).
strlen("hello\n") would return 6 and the \n is at index 5, so you only want to subtract 1.
Sorry for my ignorance but talking about gets in another topic here im wondering then why
1. fgets is better then gets if it suffers the same issues with buffer overrun
2. Why is this error checking for over run not done alredy in the fgets function. <-- explianing this more would be like why then if you pass more from the source string does it not detect this and return a error value. basily all it would need to do is compare the 2 strings and if the source is larger then the destination not preform the action and return an error and dump the source.
Maybe this isnt possible but it kinda would make better sense to do this. Hence why Getline was created albeit it still has some issues one big one being portablity.
Originally posted by itsme86 @perfect_circle: No, the correct code is the one I originally posted. You only want to subtract 1 from strlen(). If the user enters "hello" then fgets() will store this in the buffer: 'h' 'e' 'l' 'l' 'o' '\n' '\0' (assuming the buffer is sufficiently big).
strlen("hello\n") would return 6 and the \n is at index 5, so you only want to subtract 1.
I know, if you see the last edited time, my post is corrected before you posted this message.
Quote:
1. fgets is better then gets if it suffers the same issues with buffer overrun
2. Why is this error checking for over run not done alredy in the fgets function. <-- explianing this more would be like why then if you pass more from the source string does it not detect this and return a error value. basily all it would need to do is compare the 2 strings and if the source is larger then the destination not preform the action and return an error and dump the source.
1. Who said that fgets suffers from this issue?
2. fgets will get at most as many chars as the second field of the function(minus 1) implies. if the input stream contain more chars until the newline, a second fgets will get them. This is not an error. By simply checking the destination buffer you can determine what happend. If fgets worked the way you say, this would destroy fgets flexibility. fgets does not cause a buffer overrun at all if you are carefull, no matter what the input is.
Last edited by perfect_circle; 02-27-2005 at 01:02 PM.
Here's a getline() function I made that will always get all the data the user typed no matter how big the buffer is. You can also use it for reading a line from a file:
itsme@dreams:~/C$ ./getline
This line is much longer than the buffer which can only hold 10 characters. As you can see, it grabs all of it.
You typed: This line is much longer than the buffer which can only hold 10 characters. As you can see, it grabs all of it.
itsme@dreams:~/C$
All these counter points serve to illustrate problems that can and do occur when taking keyboard, or otherwise untrusted, input using typical C programming statements. GUI's and ncurses were designed to bring this user interface back into a more trusted arena.
The point we, or at least I, want to make is buffer-overruns are reality and you must consider beforehand their impact. i.e. the ultimate user will not be happy with a program that does not work.
If I cannot use ncurses or a GUI to limit the users expectation of how much to input, then I use overly sized buffers in pairs. One BIG buffer(ch_buffer) (say 512 or 1024 or even 2048 ) as the main i/o buffer to get stuff - this ensure I get it all -, then I use sscanf (or something like it) to pickout the pieces I want and place it into a regular sized buffer(ch_food). Of course I always reuse the BIG buffer for the next input, and so on.
Wasting a few hundred bytes is fine in today's megabyte+ systems, if it protects the logic of your program and the users experience. Anytime you need to do IO against untrusted interfaces (be it web or keyboard) you should consider taking extra measures to protect against overruns. I often resort to strncpy vs strcpy, snprintf vs sprintf, and ctime_r vs ctime, strtok_r vs strtok, as a few examples of what the C library makes available to give your the extra control needed.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.