Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > Standard input stream behaviour on Linux

Reply
Thread Tools

Standard input stream behaviour on Linux

 
 
Tomás Ó hÉilidhe
Guest
Posts: n/a
 
      10-22-2008

I have a fully-portable C program (or at least I think I do). It works
fine on Windows, but malfunctions on Linux. I suspect that there's
something I don't know about the standard input stream that's causing
the problem.

Here's how I wrote the program originally:

#include <stdio.h>
#include <string.h>

void TrimNewLineOffEnd(char *const str)
{
str[strlen(str) - 1] = 0;
}

int main(void)
{
char buf[20];

unsigned age, siblings;

printf("What age are you? ");

scanf("%u",&age);

printf("How many siblings do you have? ");

scanf("%u", &siblings);

printf("What's your name? ");

fgets(buf,15,stdin);

TrimNewLineOffEnd(buf);

printf("\n\nYour name is %s, you're %u years old and you have %u
siblings!\n\n",
buf,age,siblings);

return 0;
}

This original code didn't work as intended on either Windows or Linux.
On both systems, when control reached "fgets", the user wasn't given a
chance to enter their name; instead, fgets returned immediately with
an empty string.

To remedy this problem on Windows, I put in "fflush(stdin)" right
before the call to "fgets". This fixed the problem and the program
worked as intended. This didn't work on Linux however.

What am I doing wrong?
 
Reply With Quote
 
 
 
 
Ian Collins
Guest
Posts: n/a
 
      10-22-2008
Tomás Ó hÉilidhe wrote:
>
> This original code didn't work as intended on either Windows or Linux.
> On both systems, when control reached "fgets", the user wasn't given a
> chance to enter their name; instead, fgets returned immediately with
> an empty string.
>
> To remedy this problem on Windows, I put in "fflush(stdin)" right
> before the call to "fgets". This fixed the problem and the program
> worked as intended. This didn't work on Linux however.
>
> What am I doing wrong?


Calling fflush(stdin). fflush only (portably) works on output streams.

--
Ian Collins
 
Reply With Quote
 
 
 
 
Peter Nilsson
Guest
Posts: n/a
 
      10-22-2008
Tomás Ó hÉilidhe <(E-Mail Removed)> wrote:
> I have a fully-portable C program (or at least I think I
> do). It works fine on Windows, but malfunctions on Linux.
> I suspect that there's something I don't know about the
> standard input stream that's causing the problem.


You haven't read the FAQ.

> Here's how I wrote the program originally:
>
> #include <stdio.h>
> #include <string.h>
>
> void TrimNewLineOffEnd(char *const str)
> {
> * * str[strlen(str) - 1] = 0;
> }


This assumes there was a newline.

> int main(void)
> {
> * * char buf[20];
> * * unsigned age, siblings;
>
> * * printf("What age are you? ");


You should flush stdout if your prompt doesn't have
a newline.

> * * scanf("%u",&age);


You should really check the return value.

> * * printf("How many siblings do you have? ");
> * * scanf("%u", &siblings);


There is no reason for %u to convert the newline
that follows the entered number.

> * * printf("What's your name? ");
> * * fgets(buf,15,stdin);


More robust is fgets(buf, sizeof buf, stdin)

> * * TrimNewLineOffEnd(buf);


There won't necessarily be a newline on the last line
of input.

<snip>
> To remedy this problem on Windows, I put in
> "fflush(stdin)"


Google clc for comments on fflush stdin.

> right before the call to "fgets". This fixed the
> problem and the program worked as intended.


But it wasn't portable.

> This didn't work on Linux however.


Undefined behaviour rarely works on all systems.

--
Peter
 
Reply With Quote
 
dj3vande@csclub.uwaterloo.ca.invalid
Guest
Posts: n/a
 
      10-22-2008
In article <(E-Mail Removed)>,
Tomas S hIilidhe <(E-Mail Removed)> wrote:
>
>I have a fully-portable C program (or at least I think I do). It works
>fine on Windows, but malfunctions on Linux. I suspect that there's
>something I don't know about the standard input stream that's causing
>the problem.
>
>Here's how I wrote the program originally:


[...]


> printf("How many siblings do you have? ");
>
> scanf("%u", &siblings);
>
> printf("What's your name? ");
>
> fgets(buf,15,stdin);



>This original code didn't work as intended on either Windows or Linux.
>On both systems, when control reached "fgets", the user wasn't given a
>chance to enter their name; instead, fgets returned immediately with
>an empty string.
>
>To remedy this problem on Windows, I put in "fflush(stdin)" right
>before the call to "fgets". This fixed the problem and the program
>worked as intended. This didn't work on Linux however.
>
>What am I doing wrong?


(i) fflushing an input stream. This invokes undefined behavior; its
effect seems to be making it act the way you want on Windows, not
changing anything on Linux, and making daemons fly out of your nose
on the DeathStation.
(ii) Using scanf for user input. The problem you've run into is only
one of the ways it will make your life harder.

When you use scanf to read a number, it leaves everything after the end
of the number (including, in your case, the newline at the end of the
line) in the input buffer.
Then when you call fgets, it reads to the next newline... which happens
to be the one after the number scanf just read, so it doesn't need to
wait for more and returns immediately with an "empty" line.
(If you answered the siblings question with "15 Bob", the program
should conclude that your name is Bob and that you have 15 siblings.)

A better way to read user input is to *always* grab an entire line with
fgets, and then extract the information you want out of that. (sscanf
will do the job, but strtol and friends give you more control and
better error detection if you're trying to extract numbers from
strings.)

Here's an example (untested, not even compiled on my end, for
demonstration purposes only):
========
int read_int(FILE *in)
{
char buf[42];
puts("Enter a number:");
while(fgets(buf,sizeof buf,in))
{
int ret;
/*sscanf returns the number of successful conversions*/
if(sscanf(buf,"%d",&ret)==1)
return ret;
else
puts("I said a NUMBER! Try again:");
}
/*If we fall out of the loop, we got EOF or error*/
puts("Well, fine, then...");
return 0;
}
========


dave

--
Dave Vandervies dj3vande at eskimo dot com
You're right of course. Stupid mistake on my part. That'll teach me
to post while using a rented brain.
--Keith Thompson in comp.lang.c
 
Reply With Quote
 
Tomás Ó hÉilidhe
Guest
Posts: n/a
 
      10-22-2008
On Oct 23, 3:47*am, Peter Nilsson <(E-Mail Removed)> wrote:

> > void TrimNewLineOffEnd(char *const str)
> > {
> > * * str[strlen(str) - 1] = 0;
> > }

>
> This assumes there was a newline.



The Dinkumware online reference tells me that "fgets" puts a new-line
character at the end.


> > * * printf("What age are you? ");

>
> You should flush stdout if your prompt doesn't have
> a newline.



So you mean:

printf("What age are you? "); fflush(stdout);

That right?


> There is no reason for %u to convert the newline
> that follows the entered number.



So how can I call fgets after I've used scanf to take in a number?
Would the following be fully portable:

scanf("%u",&age);
getchar(); /* Take in the new-line character */
fgets(buf,15,stdin);

Just as an aside, is a new line always represented as a single byte
(i.e. '\n'), coz I've seen how on some systems you have "\r\n".
 
Reply With Quote
 
Tomás Ó hÉilidhe
Guest
Posts: n/a
 
      10-22-2008
On Oct 23, 4:02*am, (E-Mail Removed) (Gordon Burditt) wrote:

> Much like putting black tape over the "CHECK ENGINE" light fixes
> car problems.



Don't be foolish, everyone knows you just disconnect the LED when
you're selling the car
 
Reply With Quote
 
Martin Ambuhl
Guest
Posts: n/a
 
      10-22-2008
Tomás Ó hÉilidhe wrote:
> I have a fully-portable C program (or at least I think I do).


No, it isn't.
> It works
> fine on Windows, but malfunctions on Linux.


If it works 'fine' on Windows, that is an accident.

> I suspect that there's
> something I don't know about the standard input stream that's causing
> the problem.
>
> Here's how I wrote the program originally:


Here's a modified version. It is nowhere near professional standards,
but it will help you, I think.

#include <stdio.h>
#include <string.h>

/* mha: note the changed body of this function */
void TrimNewLineOffEnd(char *const str)
{
char *nl = strchr(str, '\n');
if (nl)
*nl = 0;
}

int main(void)
{
char buf[20];
unsigned age, siblings;
printf("What age are you? ");
fflush(stdout); /* mha: added */
scanf("%u", &age);

printf("How many siblings do you have? ");
fflush(stdout); /* mha: added */
scanf("%u", &siblings); /* mha: note change */

printf("What's your name? ");
fflush(stdout); /* mha: added */
fgets(buf, 15, stdin);
fgets(buf, sizeof buf, stdin); /* mha: note change */

TrimNewLineOffEnd(buf);

printf
("\n\nYour name is %s, you're %u years old "
"and you have %u siblings!\n\n", buf, age, siblings);

return 0;
}
 
Reply With Quote
 
Ian Collins
Guest
Posts: n/a
 
      10-22-2008
Tomás Ó hÉilidhe wrote:
> On Oct 23, 3:47 am, Peter Nilsson <(E-Mail Removed)> wrote:
>
>>> void TrimNewLineOffEnd(char *const str)
>>> {
>>> str[strlen(str) - 1] = 0;
>>> }

>> This assumes there was a newline.

>
>
> The Dinkumware online reference tells me that "fgets" puts a new-line
> character at the end.
>

You must have misunderstood. If a new-line character is read, it is
retained. If the line is too long, it is not added.
>
> Just as an aside, is a new line always represented as a single byte
> (i.e. '\n'), coz I've seen how on some systems you have "\r\n".


Yes. If the OS differentiates between binary and text output, the '\n'
with be modified appropriately

--
Ian Collins
 
Reply With Quote
 
Tomás Ó hÉilidhe
Guest
Posts: n/a
 
      10-22-2008
On Oct 23, 4:34*am, Martin Ambuhl <(E-Mail Removed)> wrote:

> * * *scanf("%u", &siblings); * * /* mha: note change */



Did you mean to write?:

"%u\n"
 
Reply With Quote
 
Nate Eldredge
Guest
Posts: n/a
 
      10-22-2008
Tomás Ó hÉilidhe <(E-Mail Removed)> writes:

> On Oct 23, 3:47*am, Peter Nilsson <(E-Mail Removed)> wrote:
>
>> > void TrimNewLineOffEnd(char *const str)
>> > {
>> > * * str[strlen(str) - 1] = 0;
>> > }

>>
>> This assumes there was a newline.

>
>
> The Dinkumware online reference tells me that "fgets" puts a new-line
> character at the end.


Either it's wrong, or you have a weird non-standard version of fgets.

fgets(buf, 15, stdin) could fail to put a newline at the end if (a)
stdin ends in the middle of a line, or (b) stdin contains a line longer
than 15 characters.

>> > * * printf("What age are you? ");

>>
>> You should flush stdout if your prompt doesn't have
>> a newline.

>
>
> So you mean:
>
> printf("What age are you? "); fflush(stdout);
>
> That right?


Right.

>> There is no reason for %u to convert the newline
>> that follows the entered number.

>
>
> So how can I call fgets after I've used scanf to take in a number?
> Would the following be fully portable:
>
> scanf("%u",&age);
> getchar(); /* Take in the new-line character */
> fgets(buf,15,stdin);


Ah, but the character following the number might not be a newline. The
user might type

22<space><return>

or

22 years old

Someone else suggested reading the entire line with fgets, and using
sscanf or strtol to parse it. This is a good idea in general.
scanf/fscanf are hard to use reliably unless your input is rather
strictly formatted.

> Just as an aside, is a new line always represented as a single byte
> (i.e. '\n'), coz I've seen how on some systems you have "\r\n".


Yes, that's the C definition of "newline". Systems that use "\r\n"
(e.g. DOS/Windows) will generally silently convert "\r\n" to and from
"\n" when the file is opened in text mode ("t" flag to fopen()). On
such systems it is especially important to get that mode right.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Conversion from Input Stream to Output Stream Kashif Ur Rehman Java 2 05-17-2007 07:50 PM
How to GET multi-word input from a *file* stream as opposed to a *console* stream? sherifffruitfly@gmail.com C++ 9 04-27-2006 04:14 PM
whether is the standard input stream full buffered or line buffered after calling function setbuf()? kernelxu@hotmail.com C Programming 9 08-23-2005 02:24 PM
Is input/output stream buffer behaviour random acc. to C++ standard? qazmlp1209@rediffmail.com C++ 2 05-29-2005 03:39 PM
Irregular behaviour: C++ standard lib and file stream. bruce varley C++ 2 11-25-2003 05:27 PM



Advertisments