getline - sort of

Discussion in 'C Programming' started by Bill Waddington, Jan 16, 2008.

  1. This must be a FAQ - or several FAQs - but I can't quite seem
    to pin it down.

    I need to read a string from stdin which I will then process as digits
    with sscanf. I need to limit the # of chars read, and discard the
    rest of the line. I also need to detect when more or fewer chars
    are input. Something like GNU getline I guess.

    Is there a standard portable way to do this w/a library function,
    or do I just write it myself and process input a single char at
    a time?

    This must come up all the time. Sorry to be such a pinhead. Drivers
    I can handle. User input, that another thing entirely...

    Thanks,
    Bill
    --
    William D Waddington

    "Even bugs...are unexpected signposts on
    the long road of creativity..." - Ken Burtch
    Bill Waddington, Jan 16, 2008
    #1
    1. Advertising

  2. "Bill Waddington" <> wrote in message
    > This must be a FAQ - or several FAQs - but I can't quite seem
    > to pin it down.
    >
    > I need to read a string from stdin which I will then process as digits
    > with sscanf. I need to limit the # of chars read, and discard the
    > rest of the line. I also need to detect when more or fewer chars
    > are input. Something like GNU getline I guess.
    >
    > Is there a standard portable way to do this w/a library function,
    > or do I just write it myself and process input a single char at
    > a time?
    >
    > This must come up all the time. Sorry to be such a pinhead. Drivers
    > I can handle. User input, that another thing entirely...
    >

    There's no really good way of getting an unbounded input line.

    Probably if the input is line-delimited numbers, your best bet is to call
    fgets() with a suitably over-lrage buffer.
    Reject any input for which there is no trailing newline.
    Then call strtod() to convert the .line to a number.

    char buff[256];
    double x;
    char *end;

    fgets(buff, 256, stdin);
    if(!strchr(buff, "\n'))
    {
    evil person is passing you a bad line that is over long
    }
    x = strtod(buff, &end);
    if(end == buff)
    {
    evil person passed a line that was not a number
    }

    at this point x holds the number, end the addfress of the last character
    after it. You might discard, or process further.

    --
    Free games and programming goodies.
    http://www.personal.leeds.ac.uk/~bgy1mm
    Malcolm McLean, Jan 16, 2008
    #2
    1. Advertising

  3. Bill Waddington

    Flash Gordon Guest

    Malcolm McLean wrote, On 16/01/08 17:39:
    >
    > "Bill Waddington" <> wrote in message
    >> This must be a FAQ - or several FAQs - but I can't quite seem
    >> to pin it down.
    >>
    >> I need to read a string from stdin which I will then process as digits
    >> with sscanf. I need to limit the # of chars read, and discard the
    >> rest of the line. I also need to detect when more or fewer chars
    >> are input. Something like GNU getline I guess.
    >>
    >> Is there a standard portable way to do this w/a library function,
    >> or do I just write it myself and process input a single char at
    >> a time?
    >>
    >> This must come up all the time. Sorry to be such a pinhead. Drivers
    >> I can handle. User input, that another thing entirely...
    >>

    > There's no really good way of getting an unbounded input line.
    >
    > Probably if the input is line-delimited numbers, your best bet is to
    > call fgets() with a suitably over-lrage buffer.


    Since the OP has a requirement for an upper limit an overly large buffer
    is *not* required. The OP needs a buffer of the correct size allowing
    for the newline and /0 termination.

    > Reject any input for which there is no trailing newline.


    Where did the OP say reject? The OP should use lack of a newline to
    indicate too long a line and loop to discard the rest, and an early
    newline to indicate too short a line.

    > Then call strtod() to convert the .line to a number.


    Where did the OP specify a floating point number? I agree that the strto
    functions are generally easier to use than sscanf but as the OP does not
    mention decimal points or signs strtoul is more likely to be of use.

    > char buff[256];


    Manifest constants are bad.

    > double x;
    > char *end;
    >
    > fgets(buff, 256, stdin);


    Here you repeat your manifest constant leading to a higher chance of
    introducing bugs.

    Either #define (or enum) the buffer size of use sizeof on the buffer (it
    it will be the buffer not a pointer passed in, which seems likely.

    Also fgets returns a value. Failure to check it means you will not know
    if an end-of-file or error occurs, both of which *can* occur on stdin.

    > if(!strchr(buff, "\n'))


    strchr takes the character as an int value, NOT a (pointer to) string.
    Also the OP probably wants to use the value returned to find out how
    long the line was. So that should be more like:
    eolptr = strchr(buff,'\n');
    if (!eolptr)

    > {
    > evil person is passing you a bad line that is over long


    Do a loop reading until you get either EOF or /n. I would use getc
    rather than fgetc for this since it is intended to be a macro and
    therefore makes the optimisers job easier.

    > }


    else
    linelen = eolptr-buf;

    > x = strtod(buff, &end);
    > if(end == buff)
    > {
    > evil person passed a line that was not a number


    Or something that started with something other than white space or a
    number. We do not know that this is invalid and it might be why the OP
    wants to use sscanf.

    > }
    >
    > at this point x holds the number, end the addfress of the last character
    > after it. You might discard, or process further.


    Or it could contain an INF or NAN. Also errno should be checked (having
    been zeroed prior to the call) so that overflow can easily be detected.
    --
    Flash Gordon
    Flash Gordon, Jan 16, 2008
    #3
  4. "Flash Gordon" <> wrote in message
    >
    >> at this point x holds the number, end the addfress of the last character
    >> after it. You might discard, or process further.

    >
    > Or it could contain an INF or NAN. Also errno should be checked (having
    > been zeroed prior to the call) so that overflow can easily be detected.
    >

    That's why strtod is the better choice, unless you really need massive and
    accurate integers.

    --
    Free games and programming goodies.
    http://www.personal.leeds.ac.uk/~bgy1mm
    Malcolm McLean, Jan 16, 2008
    #4
  5. Bill Waddington

    Flash Gordon Guest

    Malcolm McLean wrote, On 16/01/08 18:42:
    > "Flash Gordon" <> wrote in message
    >>
    >>> at this point x holds the number, end the addfress of the last
    >>> character after it. You might discard, or process further.

    >>
    >> Or it could contain an INF or NAN. Also errno should be checked
    >> (having been zeroed prior to the call) so that overflow can easily be
    >> detected.
    >>

    > That's why strtod is the better choice, unless you really need massive
    > and accurate integers.


    You not using strtod properly is a reason why it should be used? Strange
    argument. Anyway...

    With strtod you need to check the end pointer, errno and *also* check
    for INF and NAN (they can be explicitly supplied by the user and that
    might not be flagged). If you only expect integers you *also* have to
    check that the user entered an integer (entering 0.5 should in my
    opinion not be considered correct). You also have to be concerned about
    whether the range of exactly represented integers is large enough.

    With strtoul you have to check the end pointer and errno. If the valid
    range is smaller than unsigned long you also have to check the range.

    Hmm, a description under half the length suggests to me that it is a LOT
    simpler to use strtoul, strtoull, strtol or strtoll depending on valid
    range.

    Also you should note that strtoul etc are actually *designed* for
    integers unlike strtod. It is generally best to use a tool designed for
    the job when one is available rather than some other tool.
    --
    Flash Gordon
    Flash Gordon, Jan 16, 2008
    #5
  6. Bill Waddington

    Randy Howard Guest

    On Wed, 16 Jan 2008 11:24:21 -0600, Bill Waddington wrote
    (in article <>):

    > This must be a FAQ - or several FAQs - but I can't quite seem
    > to pin it down.
    >
    > I need to read a string from stdin which I will then process as digits
    > with sscanf. I need to limit the # of chars read, and discard the
    > rest of the line. I also need to detect when more or fewer chars
    > are input. Something like GNU getline I guess.


    Richard has this on his website, which seems to cover the general idea
    pretty well, I suspect it might be helpful to you.

    http://www.cpax.org.uk/prg/writings/fgetdata.php



    --
    Randy Howard (2reply remove FOOBAR)
    "The power of accurate observation is called cynicism by those
    who have not got it." - George Bernard Shaw
    Randy Howard, Jan 16, 2008
    #6
  7. "Flash Gordon" <> wrote in message
    > Malcolm McLean wrote, On 16/01/08 18:42:
    >> "Flash Gordon" <> wrote in message
    >>>
    >>>> at this point x holds the number, end the addfress of the last
    >>>> character after it. You might discard, or process further.
    >>>
    >>> Or it could contain an INF or NAN. Also errno should be checked (having
    >>> been zeroed prior to the call) so that overflow can easily be detected.
    >>>

    >> That's why strtod is the better choice, unless you really need massive
    >> and accurate integers.

    >
    > You not using strtod properly is a reason why it should be used? Strange
    > argument. Anyway...
    >
    > With strtoul you have to check the end pointer and errno. If the valid
    > range is smaller than unsigned long you also have to check the range.
    >
    > Hmm, a description under half the length suggests to me that it is a LOT
    > simpler to use strtoul, strtoull, strtol or strtoll depending on valid
    > range.
    >

    If the user passes NAN or INF, it is an open question what the correct
    behaviour should be. Having the double set to that value is a good start.
    If he passes something non-numeric, endptr will pick it up.
    >
    > Also you should note that strtoul etc are actually *designed* for integers
    > unlike strtod. It is generally best to use a tool designed for the job
    > when one is available rather than some other tool.
    >

    Generally, yes, you shoyuld, use functions as designed.

    In fact strtol and related functions are driven more by the idea that data
    is integers, becasue that's what the machine can crunch efficiently.
    Generally data is numerical when it is not strings. It is easier to let
    corrupt data be represented as massive flaoting point numbers which can then
    be filtered out during sanity checks further down the line, rather than try
    to build a parser for integers.

    The exception might be super-safe applications where you don't want even the
    remote chance that something like 10.000000000000001 would be changed into
    an integral 10 and accepted, when in fact you want to reject such input.
    However needing that level of safety is rare.


    --
    Free games and programming goodies.
    http://www.personal.leeds.ac.uk/~bgy1mm
    Malcolm McLean, Jan 16, 2008
    #7
  8. Bill Waddington

    Eric Sosman Guest

    Bill Waddington wrote:
    > This must be a FAQ - or several FAQs - but I can't quite seem
    > to pin it down.
    >
    > I need to read a string from stdin which I will then process as digits
    > with sscanf. I need to limit the # of chars read, and discard the
    > rest of the line. I also need to detect when more or fewer chars
    > are input. Something like GNU getline I guess.
    >
    > Is there a standard portable way to do this w/a library function,
    > or do I just write it myself and process input a single char at
    > a time?
    >
    > This must come up all the time. Sorry to be such a pinhead. Drivers
    > I can handle. User input, that another thing entirely...



    Other responders seem to be pointing to solutions of a
    related but different problem: Reading an entire line without
    knowing how long it might be. Your task seems simpler, and
    there's no need to commit canaricide with cannons.

    Since you know an upper limit on the line length, you can
    use a char array of the appropriate size (including room for
    the '\n' and the '\0'), and read a line into it with fgets().
    Then use strchr() to find the '\n' at the end of the line. If
    a newline is found, fgets() got an entire line and the position
    of the newline gives you its length. If not, the line was too
    long and part of it remains unread, or else you've reached end-
    of-input in a malformed file that lacks a '\n' at the end of its
    final line. Either way, you can skip the rest of the line (or
    detect the newline-less final line), by calling getc() or getchar()
    in a loop until it returns '\n' or EOF.

    --
    Eric Sosman, Jan 16, 2008
    #8
  9. On Wed, 16 Jan 2008 14:47:50 -0500, Eric Sosman <>
    wrote:

    >Bill Waddington wrote:
    >> This must be a FAQ - or several FAQs - but I can't quite seem
    >> to pin it down.
    >>
    >> I need to read a string from stdin which I will then process as digits
    >> with sscanf. I need to limit the # of chars read, and discard the
    >> rest of the line. I also need to detect when more or fewer chars
    >> are input. Something like GNU getline I guess.
    >>
    >> Is there a standard portable way to do this w/a library function,
    >> or do I just write it myself and process input a single char at
    >> a time?
    >>
    >> This must come up all the time. Sorry to be such a pinhead. Drivers
    >> I can handle. User input, that another thing entirely...

    >
    >
    >Other responders seem to be pointing to solutions of a
    >related but different problem: Reading an entire line without
    >knowing how long it might be.


    True. I know how long the useful input might be, but need to
    allow for less or more and discard any extra input safely.

    >Your task seems simpler, and there's no need to commit
    >canaricide with cannons.


    >Since you know an upper limit on the line length, you can
    >use a char array of the appropriate size (including room for
    >the '\n' and the '\0'), and read a line into it with fgets().
    >Then use strchr() to find the '\n' at the end of the line. If
    >a newline is found, fgets() got an entire line and the position
    >of the newline gives you its length. If not, the line was too
    >long and part of it remains unread, or else you've reached end-
    >of-input in a malformed file that lacks a '\n' at the end of its
    >final line. Either way, you can skip the rest of the line (or
    >detect the newline-less final line), by calling getc() or getchar()
    >in a loop until it returns '\n' or EOF.


    That, or just grind along w/getc() or getchar() from the start.

    If I read all the suggestions correctly, the bottom line is code
    it up or borrow it somewhere, but there isn't a single standard
    lib way to do it.

    Thanks to all,
    Bill
    --
    William D Waddington

    "Even bugs...are unexpected signposts on
    the long road of creativity..." - Ken Burtch
    Bill Waddington, Jan 16, 2008
    #9
  10. Bill Waddington

    Flash Gordon Guest

    Malcolm McLean wrote, On 16/01/08 19:16:
    >
    > "Flash Gordon" <> wrote in message
    >> Malcolm McLean wrote, On 16/01/08 18:42:
    >>> "Flash Gordon" <> wrote in message
    >>>>
    >>>>> at this point x holds the number, end the addfress of the last
    >>>>> character after it. You might discard, or process further.
    >>>>
    >>>> Or it could contain an INF or NAN. Also errno should be checked
    >>>> (having been zeroed prior to the call) so that overflow can easily
    >>>> be detected.
    >>>>
    >>> That's why strtod is the better choice, unless you really need
    >>> massive and accurate integers.

    >>
    >> You not using strtod properly is a reason why it should be used?
    >> Strange argument. Anyway...
    >>
    >> With strtoul you have to check the end pointer and errno. If the valid
    >> range is smaller than unsigned long you also have to check the range.
    >>
    >> Hmm, a description under half the length suggests to me that it is a
    >> LOT simpler to use strtoul, strtoull, strtol or strtoll depending on
    >> valid range.
    >>

    > If the user passes NAN or INF, it is an open question what the correct
    > behaviour should be. Having the double set to that value is a good start.
    > If he passes something non-numeric, endptr will pick it up.
    >>
    >> Also you should note that strtoul etc are actually *designed* for
    >> integers unlike strtod. It is generally best to use a tool designed
    >> for the job when one is available rather than some other tool.
    >>

    > Generally, yes, you shoyuld, use functions as designed.
    >
    > In fact strtol and related functions are driven more by the idea that
    > data is integers, becasue that's what the machine can crunch efficiently.


    No, they are driven by the idea the there are a lot of situations where
    you are dealing with integers.

    > Generally data is numerical when it is not strings.


    I do a lot of interfacing with other systems. A lot of the time the
    numbers are provided as integers (numbers of pennies etc.)

    > It is easier to let
    > corrupt data be represented as massive flaoting point numbers which can
    > then be filtered out during sanity checks further down the line, rather
    > than try to build a parser for integers.


    You do not have to build a parser for integers, those nice library
    writers have done the job for you.

    > The exception might be super-safe applications where you don't want even
    > the remote chance that something like 10.000000000000001 would be
    > changed into an integral 10 and accepted, when in fact you want to
    > reject such input. However needing that level of safety is rare.


    No, the exception is where the requirement is to accept an integer, and
    this is a very common situation.
    --
    Flash Gordon
    Flash Gordon, Jan 16, 2008
    #10
  11. Bill Waddington

    Bart C Guest

    "Bill Waddington" <> wrote in message
    news:...
    > This must be a FAQ - or several FAQs - but I can't quite seem
    > to pin it down.
    >
    > I need to read a string from stdin which I will then process as digits
    > with sscanf. I need to limit the # of chars read, and discard the
    > rest of the line. I also need to detect when more or fewer chars
    > are input. Something like GNU getline I guess.
    >
    > Is there a standard portable way to do this w/a library function,
    > or do I just write it myself and process input a single char at
    > a time?
    >
    > This must come up all the time. Sorry to be such a pinhead. Drivers
    > I can handle. User input, that another thing entirely...


    As I understand this you have input like:

    10
    20 30
    40 50 60
    70 80 90 100
    110 120 130 140 150

    And if you read this using this pseudocode:

    while !eof /* note this is not how C uses eof! */
    { readln /* read as much of the next input line as needed, discard rest
    */
    read A,B,C /* read your data from the buffer, as integers */
    }

    Then A,B,C will successively be (10,0,0), (20,30,0), (40,50,60), (70,80,90),
    (110,120,130). And with unused input on each line of "", "", "", "100","140
    150".

    Then you will have an input system like I use, where Newline gives a 'hard'
    end to the input (so read attempts give 0 or 0.0 or "") instead of being
    ignored as white space and continuing to the next line.

    However imposing this behaviour on the C library routines was a quite a bit
    of work
    (I call them from another language).

    The 'readln' can be done according to Eric Sosman's message, that's quite
    well-defined. Whatever else you need is just a bunch of routines; sorry
    can't point to existing code. But once you know the next input line is in a
    memory string and zero-terminated, then this makes life easier.

    Bart
    Bart C, Jan 16, 2008
    #11
  12. Bill Waddington

    CBFalconer Guest

    Bill Waddington wrote:
    >
    > This must be a FAQ - or several FAQs - but I can't quite seem
    > to pin it down.
    >
    > I need to read a string from stdin which I will then process as
    > digits with sscanf. I need to limit the # of chars read, and
    > discard the rest of the line. I also need to detect when more
    > or fewer chars are input. Something like GNU getline I guess.
    >
    > Is there a standard portable way to do this w/a library function,
    > or do I just write it myself and process input a single char at
    > a time?
    >
    > This must come up all the time. Sorry to be such a pinhead.
    > Drivers I can handle. User input, that another thing entirely.


    Dead simple. Use the standard functions described in ctype.h to
    isolate the chars. Count chars any way you wish. Use getc() (or
    getchar()) to input chars. Don't use sscanf, it is not suitable
    for interactive work. Do use ungetc() to put back the terminating
    (unused) char.

    --
    [mail]: Chuck F (cbfalconer at maineline dot net)
    [page]: <http://cbfalconer.home.att.net>
    Try the download section.



    --
    Posted via a free Usenet account from http://www.teranews.com
    CBFalconer, Jan 16, 2008
    #12
  13. Bill Waddington

    CBFalconer Guest

    Malcolm McLean wrote:
    > "Bill Waddington" <> wrote in message
    >

    .... snip ...
    >
    >> This must come up all the time. Sorry to be such a pinhead.
    >> Drivers I can handle. User input, that another thing entirely.

    >
    > There's no really good way of getting an unbounded input line.


    Yes there is. That is what streams are for.

    --
    [mail]: Chuck F (cbfalconer at maineline dot net)
    [page]: <http://cbfalconer.home.att.net>
    Try the download section.



    --
    Posted via a free Usenet account from http://www.teranews.com
    CBFalconer, Jan 16, 2008
    #13
  14. Bill Waddington

    pete Guest

    Bill Waddington wrote:

    > If I read all the suggestions correctly, the bottom line is code
    > it up or borrow it somewhere, but there isn't a single standard
    > lib way to do it.


    That's it.

    I have a way to read lines from stdin and discard input
    beyond the buffer length, using fscanf:

    http://www.mindspring.com/~pfilandr/C/fscanf_input/fscanf_input.c
    http://www.mindspring.com/~pfilandr/C/fscanf_input/grade.c

    but that way doesn't tell you
    whether or not any bytes were discarded.

    I have a get_line function, but it always gets complete lines,
    to the extent that the getc and realloc calls that it uses,
    succede.

    http://www.mindspring.com/~pfilandr/C/get_line/get_line.c

    It does however, have a some diagnostic capability built in.
    Now With New And Improved Bigger Comment!

    /*
    ** int get_line(char **lineptr, size_t *n, FILE *stream);
    **
    ** Requirements for initial parameter values:
    ** (*lineptr) is either
    ** the address of the start of a block of allocated memory,
    ** or a null pointer;
    ** (*n) is the number of bytes that (*lineptr) points to;
    ** (*stream) is associated with a text stream
    ** that is open for reading.
    **
    ** The definition of get_line uses the following headers:
    ** <stdio.h> <stdlib.h> <limits.h>
    **
    ** Side effects:
    ** get_line reads a line from a text stream using getc
    ** and writes the values of the characters in order,
    ** as an array of char,
    ** replacing the newline character with a null character.
    ** get_line uses realloc to allocate enough memory
    ** so that if a subsequent call to realloc were to fail,
    ** then *lineptr would still point to enough memory
    ** to hold a null terminated array of all of the characters
    ** which were read during the get_line function call.
    ** get_line returns if there is an end-of-file condition
    ** or a read error.
    ** get_line returns if realloc returns a null pointer.
    ** get_line returns if getc returns '\n'.
    ** get_line returns if ((size_t)-2) characters
    ** of text line have been read.
    ** get_line updates *lineptr and *n, so that *n
    ** is always the number of bytes that *lineptr points to.
    ** If *n is greater than one when get_line returns,
    ** then get_line will make a null terminated array
    ** out of the sequence of values read from the text stream.
    ** If *n equals one, and realloc returns NULL,
    ** then a null character will be written to **lineptr.
    ** If *n equals either zero or one, and realloc returns NULL,
    ** then the character which was read,
    ** will be pushed back by a call to ungetc.
    **
    ** Return values:
    ** get_line returns EOF,
    ** if there is an end-of-file condition or a read error;
    ** get_line returns zero,
    ** if realloc returns NULL;
    ** otherwise,
    ** get_line returns the number of bytes read,
    ** if the number of bytes read, including the newline,
    ** is less than INT_MAX;
    ** get_line returns INT_MAX,
    ** if the number of bytes read, including the newline,
    ** is greater than or equal to INT_MAX.
    **
    **
    ** For this expression:
    ** (buff = NULL, size = 0, rc = get_line(&buff, &size, fp))
    ** most of what there is to know
    ** about the status of the function call, can be determined
    ** from the values of some or all of the following expressions:
    ** (rc), (feof(fp)), (buff), (*buff), (size)
    ** If the text file is known to not contain null bytes,
    ** then the value of (strlen(buff)) can also be used
    ** to determine the rest of what there is to know
    ** about the status of the function call.
    ** Two items of primary interest are:
    ** 1 Was the return normal? (rc != 0 && !ferror(fp))
    ** 2 How much of a text line was read? (none, partial, complete)
    **
    ** If rc equals EOF, then:
    ** if and only if no bytes were read because of
    ** Normal Return At End Of Text File, then:
    ** ((buff == NULL || *buff == '\0') && feof(fp));
    ** if and only if no bytes were read because of a
    ** Read Error, then:
    ** ((buff == NULL || *buff == '\0') && !feof(fp));
    ** if and only if a partial line was read because of a
    ** Read Error, then:
    ** (buff != NULL && *buff != '\0' && !feof(fp));
    ** if and only if a whole line was read because of
    ** Text File Not Newline Terminated, then:
    ** (buff != NULL && *buff != '\0' && feof(fp)).
    **
    ** If rc equals zero, then:
    ** if and only if no bytes were read, then:
    ** (2 > size);
    ** if and only if a partial line was read, then:
    ** (size > 1 && size == strlen(buff) + 1);
    ** if and only if a whole line was read:
    ** (size > 1 && size > strlen(buff) + 1).
    **
    ** If rc is positive, then:
    ** if and only if a partial line was read, then:
    ** ((size_t)-1 == size && (size_t)-2 == strlen(buff));
    ** if and only if a whole line was read, then:
    ** ((size_t)-1 != size || (size_t)-2 > strlen(buff)).
    **
    ** How much of all of that information needs to be checked,
    ** depends on circumstances:
    ** The function can be used to read a whole file
    ** even though realloc keeps returning NULL,
    ** by keeping track of partial lines;
    ** or
    ** Reading the text file can be abandoned
    ** at the first sign of trouble, such as when (1 > rc),
    ** with little or no further investigation.
    */

    --
    pete
    pete, Jan 16, 2008
    #14
  15. Bill Waddington

    CBFalconer Guest

    Bill Waddington wrote:
    >
    > This must be a FAQ - or several FAQs - but I can't quite seem
    > to pin it down.
    >
    > I need to read a string from stdin which I will then process as
    > digits with sscanf. I need to limit the # of chars read, and
    > discard the rest of the line. I also need to detect when more
    > or fewer chars are input. Something like GNU getline I guess.
    >
    > Is there a standard portable way to do this w/a library function,
    > or do I just write it myself and process input a single char at
    > a time?
    >
    > This must come up all the time. Sorry to be such a pinhead.
    > Drivers I can handle. User input, that another thing entirely.


    Try this out. It would be handier if changed to input longs. I
    have relaxed the GPL licensing.

    /* ------------------------------------------------- *
    * File txtinput.c *
    * ------------------------------------------------- */

    #include <limits.h> /* xxxx_MAX, xxxx_MIN */
    #include <ctype.h> /* isdigit, isblank, isspace */
    #include <stdio.h> /* FILE, getc, ungetc */
    #include "txtinput.h"

    /* For licensing restrictions (GPL) see readme.txt in:
    * <http://cbfalconer.home.att.net/download/txtio.zip>
    *
    * These stream input routines are written so that simple
    * conditionals can be used:
    *
    * if (readxint(&myint, stdin)) {
    * do_error_recovery; normally_abort_to_somewhere;
    * }
    * else {
    * do_normal_things; usually_much_longer_than_bad_case;
    * }
    *
    * They allow overflow detection, and permit other routines to
    * detect the character that terminated a numerical field. No
    * string storage is required, thus there is no limitation on
    * the length of input fields. For example, a number entered
    * with a string of 1000 leading zeroes will not annoy these.
    *
    * The numerical input routines *NEVER* absorb a terminating
    * char (including '\n'). Thus a sequence such as:
    *
    * err = readxint(&myint, stdin);
    * flushln(stdin);
    *
    * will always consume complete lines, and after execution of
    * readxint a further getc (or fgetc) will return the character
    * that terminated the numeric field.
    *
    * They are also re-entrant, subject to the limitations of file
    * systems. e.g interrupting readxint(v, stdin) operation with
    * a call to readxwd(wd, stdin) would not be well defined, if
    * the same stdin is being used for both calls. If ungetc is
    * interruptible the run-time system is broken.
    *
    * Originally issued 2002-10-07
    *
    * Revised 2006-01-15 so that unsigned entry overflow (readxwd)
    uses the normal C modulo (UINT_MAX + 1) operation. readxwd
    still rejects an initial sign as an error.
    *
    * Modified to allow free use - C.B. Falconer 2008-01-16
    */

    /* -------------------------------------------------------------
    * Skip to non-blank on f, and return that char. or EOF The next
    * char that getc(f) will return is unknown. Local use only.
    */
    static int ignoreblks(FILE *f)
    {
    int ch;

    do {
    ch = getc(f);
    } while ((' ' == ch) || ('\t' == ch));
    /* while (isblank(ch)); */ /* for C99 */
    return ch;
    } /* ignoreblks */

    /*--------------------------------------------------------------
    * Skip all blanks on f. At completion getc(f) will return
    * a non-blank character, which may be \n or EOF
    *
    * Skipblks returns the char that getc will next return, or EOF.
    */
    int skipblks(FILE *f)
    {
    return ungetc(ignoreblks(f), f);
    } /* skipblks */

    /*--------------------------------------------------------------
    * Skip all whitespace on f, including \n, \f, \v, \r. At
    * completion getc(f) will return a non-blank character, which
    * may be EOF
    *
    * Skipwhite returns the char that getc will next return, or EOF.
    */
    int skipwhite(FILE *f)
    {
    int ch;

    do {
    ch = getc(f);
    } while (isspace(ch));
    return ungetc(ch, f);
    } /* skipwhite */

    /*--------------------------------------------------------------
    * Read an unsigned value. Signal error for overflow or no
    * valid number found. Returns 1 for error, 0 for noerror, EOF
    * for EOF encountered before parsing a value.
    *
    * Skip all leading blanks on f. At completion getc(f) will
    * return the character terminating the number, which may be \n
    * or EOF among others. Barring EOF it will NOT be a digit. The
    * combination of error, 0 result, and the next getc returning
    * \n indicates that no numerical value was found on the line.
    *
    * If the user wants to skip all leading white space including
    * \n, \f, \v, \r, he should first call "skipwhite(f);"
    *
    * Peculiarity: This specifically forbids a leading '+' or '-'.
    */
    int readxwd(unsigned int *wd, FILE *f)
    {
    unsigned int value, digit;
    int status;
    int ch;

    #define UWARNLVL (UINT_MAX / 10U)
    #define UWARNDIG (UINT_MAX - UWARNLVL * 10U)

    value = 0; /* default */
    status = 1; /* default error */

    ch = ignoreblks(f);

    if (EOF == ch) status = EOF;
    else if (isdigit(ch)) status = 0; /* digit, no error */

    while (isdigit(ch)) {
    digit = ch - '0';
    if ((value > UWARNLVL) ||
    ((UWARNLVL == value) && (digit > UWARNDIG))) {
    status = 1; /* overflow */
    value -= UWARNLVL;
    }
    value = 10 * value + digit;
    ch = getc(f);
    } /* while (ch is a digit) */

    *wd = value;
    ungetc(ch, f);
    return status;
    } /* readxwd */

    /*--------------------------------------------------------------
    * Read a signed value. Signal error for overflow or no valid
    * number found. Returns true for error, false for noerror. On
    * overflow either INT_MAX or INT_MIN is returned in *val.
    *
    * Skip all leading blanks on f. At completion getc(f) will
    * return the character terminating the number, which may be \n
    * or EOF among others. Barring EOF it will NOT be a digit. The
    * combination of error, 0 result, and the next getc returning
    * \n indicates that no numerical value was found on the line.
    *
    * If the user wants to skip all leading white space including
    * \n, \f, \v, \r, he should first call "skipwhite(f);"
    *
    * Peculiarity: an isolated leading '+' or '-' NOT immediately
    * followed by a digit will return error and a value of 0, when
    * the next getc will return that following non-digit. This is
    * caused by the single level ungetc available.
    *
    * This module needs further work. CBF 2008-01-16
    */
    int readxint(int *val, FILE *f)
    {
    unsigned int value;
    int status, negative;
    int ch;

    *val = value = 0; /* default */
    status = 1; /* default error */
    negative = 0;

    ch = ignoreblks(f);

    if (EOF != ch) {
    if (('+' == ch) || ('-' == ch)) {
    negative = ('-' == ch);
    ch = ignoreblks(f); /* absorb any sign */
    }

    if (isdigit(ch)) { /* digit, no error */
    ungetc(ch, f);
    status = readxwd(&value, f);
    ch = getc(f); /* This terminated readxwd */
    }

    if (0 == status) {
    /* got initial digit and no readxwd overflow */
    if (!negative && (value <= INT_MAX))
    *val = value;
    else if (negative && (value < UINT_MAX) &&
    ((value - 1) <= -(1 + INT_MIN)))
    *val = -value;
    else { /* overflow */
    status = 1; /* do whatever the native system does */
    if (negative) *val = -value;
    else *val = value;
    }
    }
    else if (negative) *val = -value;
    else *val = value;
    }
    ungetc(ch, f);
    return status;
    } /* readxint */

    /*-----------------------------------------------------
    * Flush input through an end-of-line marker inclusive.
    */
    void flushln(FILE *f)
    {
    int ch;

    do {
    ch = getc(f);
    } while (('\n' != ch) && (EOF != ch));
    } /* flushln */

    /* End of txtinput.c */


    /* file txtinput.h */
    #ifndef H_txtinput_h
    #define H_txtinput_h
    # ifdef __cplusplus
    extern "C" {
    # endif

    #include <stdio.h>

    /* For licensing restrictions (GPL) see readme.txt in:
    * <http://cbfalconer.home.att.net/download/txtio.zip>
    *
    * These stream input routines are written so that simple
    * conditionals can be used:
    *
    * if (readxint(&myint, stdin)) {
    * do_error_recovery; normally_abort_to_somewhere;
    * }
    * else {
    * do_normal_things; usually_much_longer_than_bad_case;
    * }
    *
    * They allow overflow detection, and permit other routines to
    * detect the character that terminated a numerical field. No
    * string storage is required, thus there is no limitation on
    * the length of input fields. For example, a number entered
    * with a string of 1000 leading zeroes will not annoy these.
    *
    * The numerical input routines *NEVER* absorb a terminating
    * char (including '\n'). Thus a sequence such as:
    *
    * err = readxint(&myint, stdin);
    * flushln(stdin);
    *
    * will always consume complete lines, and after execution of
    * readxint a further getc (or fgetc) will return the character
    * that terminated the numeric field.
    *
    * They are also re-entrant, subject to the limitations of file
    * systems. e.g interrupting readxint(v, stdin) operation with
    * a call to readxwd(wd, stdin) would not be well defined, if
    * the same stdin is being used for both calls. If ungetc is
    * interruptible the run-time system is broken.

    * Revised 2006-01-15 so that unsigned entry overflow (readxwd)
    uses the normal C modulo (UINT_MAX + 1) operation. readxwd
    still rejects an initial sign as an error.
    *
    * Modified to allow free use - C.B. Falconer 2008-01-16
    */

    /*--------------------------------------------------------------
    * Skip all blanks on f. At completion getc(f) will return
    * a non-blank character, which may be \n or EOF
    *
    * Skipblks returns the char that getc will next return, or EOF.
    */
    int skipblks(FILE *f);

    /*--------------------------------------------------------------
    * Skip all whitespace on f, including \n, \f, \v, \r. At
    * completion getc(f) will return a non-blank character, which
    * may be EOF
    *
    * Skipblks returns the char that getc will next return, or EOF.
    */
    int skipwhite(FILE *f);

    /*--------------------------------------------------------------
    * Read an unsigned value. Signal error for overflow or no
    * valid number found. Returns 1 for error, 0 for noerror, EOF
    * for EOF encountered before parsing a value.
    *
    * Skip all leading blanks on f. At completion getc(f) will
    * return the character terminating the number, which may be \n
    * or EOF among others. Barring EOF it will NOT be a digit. The
    * combination of error, 0 result, and the next getc returning
    * \n indicates that no numerical value was found on the line.
    *
    * If the user wants to skip all leading white space including
    * \n, \f, \v, \r, he should first call "skipwhite(f);"
    *
    * Peculiarity: This specifically forbids a leading '+' or '-'.
    */
    int readxwd(unsigned int *wd, FILE *f);

    /*--------------------------------------------------------------
    * Read a signed value. Signal error for overflow or no valid
    * number found. Returns true for error, false for noerror. On
    * overflow either INT_MAX or INT_MIN is returned in *val.
    *
    * Skip all leading blanks on f. At completion getc(f) will
    * return the character terminating the number, which may be \n
    * or EOF among others. Barring EOF it will NOT be a digit. The
    * combination of error, 0 result, and the next getc returning
    * \n indicates that no numerical value was found on the line.
    *
    * If the user wants to skip all leading white space including
    * \n, \f, \v, \r, he should first call "skipwhite(f);"
    *
    * Peculiarity: an isolated leading '+' or '-' NOT immediately
    * followed by a digit will return error and a value of 0, when
    * the next getc will return that following non-digit. This is
    * caused by the single level ungetc available.
    */
    int readxint(int *val, FILE *f);

    /*--------------------------------------------------------------
    * Flush input through an end-of-line marker inclusive.
    */
    void flushln(FILE *f);

    # ifdef __cplusplus
    }
    # endif
    #endif
    /* End of txtinput.h */

    --
    [mail]: Chuck F (cbfalconer at maineline dot net)
    [page]: <http://cbfalconer.home.att.net>
    Try the download section.



    --
    Posted via a free Usenet account from http://www.teranews.com
    CBFalconer, Jan 16, 2008
    #15
  16. On Wed, 16 Jan 2008 17:42:01 -0500, pete <>
    wrote:

    >Bill Waddington wrote:
    >
    >> If I read all the suggestions correctly, the bottom line is code
    >> it up or borrow it somewhere, but there isn't a single standard
    >> lib way to do it.

    >
    >That's it.
    >
    >I have a way to read lines from stdin and discard input
    >beyond the buffer length, using fscanf:
    >
    > http://www.mindspring.com/~pfilandr/C/fscanf_input/fscanf_input.c
    > http://www.mindspring.com/~pfilandr/C/fscanf_input/grade.c
    >
    >but that way doesn't tell you
    >whether or not any bytes were discarded.
    >
    >I have a get_line function, but it always gets complete lines,
    >to the extent that the getc and realloc calls that it uses,
    >succede.
    >
    > http://www.mindspring.com/~pfilandr/C/get_line/get_line.c
    >
    >It does however, have a some diagnostic capability built in.
    >Now With New And Improved Bigger Comment!


    [some snippage - see upthread]

    Hi Pete,

    Thanks for that. I appreciate all the suggestions. What
    I'm doing now is as below. It isn't really worth posting here
    but since my C skills are nill, I wouldn't mind general
    comments and error messages. It seems to be doing what I
    need for now.


    int get_line(char *buff, int maxbuff)
    {
    int good_chars = 0, all_chars = 0, inchar;

    while(1) {
    inchar = getchar();
    if((inchar == EOF) || (inchar == '\n'))
    break;
    if(good_chars < (maxbuff - 1)) {
    *(buff + good_chars) = (char)inchar;
    good_chars++;
    }
    all_chars++;
    }
    *(buff + good_chars) = '\0';
    return all_chars;
    }


    Thanks, and sorry for the noise,
    Bill
    --
    William D Waddington

    "Even bugs...are unexpected signposts on
    the long road of creativity..." - Ken Burtch
    Bill Waddington, Jan 16, 2008
    #16
  17. Bill Waddington

    pete Guest

    Bill Waddington wrote:

    > Thanks for that. I appreciate all the suggestions. What
    > I'm doing now is as below. It isn't really worth posting here
    > but since my C skills are nill, I wouldn't mind general
    > comments and error messages. It seems to be doing what I
    > need for now.
    >
    > int get_line(char *buff, int maxbuff)
    > {
    > int good_chars = 0, all_chars = 0, inchar;
    >
    > while(1) {
    > inchar = getchar();
    > if((inchar == EOF) || (inchar == '\n'))
    > break;
    > if(good_chars < (maxbuff - 1)) {
    > *(buff + good_chars) = (char)inchar;
    > good_chars++;
    > }
    > all_chars++;
    > }
    > *(buff + good_chars) = '\0';
    > return all_chars;
    > }


    I think it looks like it will do what you want.

    --
    pete
    pete, Jan 16, 2008
    #17
  18. CBFalconer said:

    <snip>

    > Don't use sscanf, it is not suitable for interactive work.


    Why not?

    --
    Richard Heathfield <http://www.cpax.org.uk>
    Email: -http://www. +rjh@
    Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
    "Usenet is a strange place" - dmr 29 July 1999
    Richard Heathfield, Jan 17, 2008
    #18
  19. Richard Heathfield <> writes:
    > CBFalconer said:
    >
    > <snip>
    >
    >> Don't use sscanf, it is not suitable for interactive work.

    >
    > Why not?


    For one thing, its behavior on numeric input overflow is undefined.

    --
    Keith Thompson (The_Other_Keith) <>
    Nokia
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Jan 17, 2008
    #19
  20. Keith Thompson said:

    > Richard Heathfield <> writes:
    >> CBFalconer said:
    >>
    >> <snip>
    >>
    >>> Don't use sscanf, it is not suitable for interactive work.

    >>
    >> Why not?

    >
    > For one thing, its behavior on numeric input overflow is undefined.


    But you can use field width specifiers to prevent this. See, for instance,
    the C89 example of fscanf (which is obviously analogous to sscanf):

    ++++++++++++++++++++++
    fscanf(stdin, "%2d%f%*d %[0123456789]", &i, &x, name);

    with input:

    56789 0123 56a72

    will assign to i the value 56
    ++++++++++++++++++++++

    Not that I'm rootin' for *scanf or anything. But of the *scanf family,
    sscanf is certainly the least useless, and it seems to me that it makes no
    odds whether the string on which it works comes from an "interactive"
    source or not.

    --
    Richard Heathfield <http://www.cpax.org.uk>
    Email: -http://www. +rjh@
    Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
    "Usenet is a strange place" - dmr 29 July 1999
    Richard Heathfield, Jan 17, 2008
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. nobody
    Replies:
    0
    Views:
    528
    nobody
    Jun 1, 2004
  2. JerryJ
    Replies:
    11
    Views:
    1,383
    Dave Moore
    Apr 28, 2004
  3. John Black
    Replies:
    6
    Views:
    2,042
    John Harrison
    May 28, 2004
  4. Angus Comber
    Replies:
    7
    Views:
    1,142
    Richard Heathfield
    Feb 5, 2004
  5. Navin
    Replies:
    1
    Views:
    668
    Ken Schaefer
    Sep 9, 2003
Loading...

Share This Page