sscanf fixed-width integer question

Discussion in 'C Programming' started by Brent Lievers, Apr 22, 2004.

  1. Greetings,

    I have a question about parsing a fixed-width integer from a string.

    Lines of data are being read from a file having a very strict
    column-delimited format. In my example below, columns 0-7 are an integer
    and columns 8-23 are a float. In _most_ files, the first few columns of
    the float are blank space to make it human readable. But not always.
    So, once they have been read from the file, I try to parse out the
    different values using something like the following:

    /*------------------------------------------*/
    #include <stdio.h>
    #include <stdlib.h>

    int main()
    {
    char line1[]= " 12 15.796473824857";
    char line2[]= " 1215.7964738248571";
    int id;
    float num;

    sscanf(line1, "%8d%16E", &id, &num);
    printf("ID= %d\n", id);
    printf("NUM= %f\n\n", num);

    sscanf(line2, "%8d%16E", &id, &num);
    printf("ID= %d\n", id);
    printf("NUM= %f\n", num);

    return EXIT_SUCCESS;
    }
    /*------------------------------------------*/

    which works in one case and not the other.

    I could read the first 8 columns as characters and strtol() them to an
    integer. Is there another/better way?

    Thanks,

    Brent
    Brent Lievers, Apr 22, 2004
    #1
    1. Advertising

  2. Brent Lievers wrote:
    > Greetings,
    >
    > I have a question about parsing a fixed-width integer from a string.
    >
    > Lines of data are being read from a file having a very strict
    > column-delimited format. In my example below, columns 0-7 are an integer
    > and columns 8-23 are a float. In _most_ files, the first few columns of
    > the float are blank space to make it human readable. But not always.
    > So, once they have been read from the file, I try to parse out the
    > different values using something like the following:
    >
    > /*------------------------------------------*/
    > #include <stdio.h>
    > #include <stdlib.h>
    >
    > int main()
    > {
    > char line1[]= " 12 15.796473824857";
    > char line2[]= " 1215.7964738248571";
    > int id;
    > float num;
    >
    > sscanf(line1, "%8d%16E", &id, &num);
    > printf("ID= %d\n", id);
    > printf("NUM= %f\n\n", num);
    >
    > sscanf(line2, "%8d%16E", &id, &num);
    > printf("ID= %d\n", id);
    > printf("NUM= %f\n", num);
    >
    > return EXIT_SUCCESS;
    > }
    > /*------------------------------------------*/
    >
    > which works in one case and not the other.
    >
    > I could read the first 8 columns as characters and strtol() them to an
    > integer. Is there another/better way?


    You could temporarily insert a '\0' at position 8 (after
    first saving its value of course). Then sscanf() the int.
    Subsequently restore the saved position 8, and sscanf() the
    float from position 8.

    Cheers,

    Kees
    - Kees van der Bent -, Apr 22, 2004
    #2
    1. Advertising

  3. - Kees van der Bent - wrote:
    > Brent Lievers wrote:
    >
    >> Greetings,
    >>
    >> I have a question about parsing a fixed-width integer from a string.
    >> Lines of data are being read from a file having a very strict
    >> column-delimited format. In my example below, columns 0-7 are an integer
    >> and columns 8-23 are a float. In _most_ files, the first few columns of
    >> the float are blank space to make it human readable. But not always.
    >> So, once they have been read from the file, I try to parse out the
    >> different values using something like the following:
    >>
    >> /*------------------------------------------*/
    >> #include <stdio.h>
    >> #include <stdlib.h>
    >>
    >> int main()
    >> {
    >> char line1[]= " 12 15.796473824857";
    >> char line2[]= " 1215.7964738248571";
    >> int id;
    >> float num;
    >>
    >> sscanf(line1, "%8d%16E", &id, &num);
    >> printf("ID= %d\n", id);
    >> printf("NUM= %f\n\n", num);
    >>
    >> sscanf(line2, "%8d%16E", &id, &num);
    >> printf("ID= %d\n", id);
    >> printf("NUM= %f\n", num);
    >>
    >> return EXIT_SUCCESS;
    >> }
    >> /*------------------------------------------*/
    >>
    >> which works in one case and not the other.
    >>
    >> I could read the first 8 columns as characters and strtol() them to an
    >> integer. Is there another/better way?

    >
    >
    > You could temporarily insert a '\0' at position 8 (after
    > first saving its value of course). Then sscanf() the int.
    > Subsequently restore the saved position 8, and sscanf() the
    > float from position 8.


    By reversing the scan order (float first), you don't even have
    to restore position 8.

    Kees
    - Kees van der Bent -, Apr 22, 2004
    #3
  4. Brent Lievers

    Eric Sosman Guest

    Brent Lievers wrote:
    >
    > Greetings,
    >
    > I have a question about parsing a fixed-width integer from a string.
    >
    > Lines of data are being read from a file having a very strict
    > column-delimited format. In my example below, columns 0-7 are an integer
    > and columns 8-23 are a float. In _most_ files, the first few columns of
    > the float are blank space to make it human readable. But not always.
    > So, once they have been read from the file, I try to parse out the
    > different values using something like the following:
    >
    > /*------------------------------------------*/
    > #include <stdio.h>
    > #include <stdlib.h>
    >
    > int main()
    > {
    > char line1[]= " 12 15.796473824857";
    > char line2[]= " 1215.7964738248571";
    > int id;
    > float num;
    >
    > sscanf(line1, "%8d%16E", &id, &num);
    > printf("ID= %d\n", id);
    > printf("NUM= %f\n\n", num);
    >
    > sscanf(line2, "%8d%16E", &id, &num);
    > printf("ID= %d\n", id);
    > printf("NUM= %f\n", num);
    >
    > return EXIT_SUCCESS;
    > }
    > /*------------------------------------------*/
    >
    > which works in one case and not the other.
    >
    > I could read the first 8 columns as characters and strtol() them to an
    > integer. Is there another/better way?


    The problem is with those leading blank spaces: the
    "%8d" conversion swallows them *without* counting them
    as part of the 8-character field width, so the 8-character
    count begins with the first digit encountered. Since the
    number of leading spaces (and other whitespace, like the
    '\n' at the end of a preceding input line) is variable, you
    don't know what field width to use to make the conversion
    stop at the desired spot. I can think of no way to get this
    to work with just one application of sscanf().

    If I were doing this, I'd start by reading the entire line
    into a buffer. Then I'd use strtod() to convert the second
    number first. Then I'd stuff a '\0' into the buffer at the
    eighth position and use strtol() to convert the first number.
    (You could use sscanf() in place of both strxxx() functions,
    if desired.)

    By the way, what's the "%16E" conversion? I've assumed
    above that it's something akin to "%16e", but if it's more
    esoteric you may need something fancier than strtod() to
    imitate it.

    --
    Eric Sosman, Apr 22, 2004
    #4
  5. Brent Lievers

    Dan Pop Guest

    In <c68p9l$ech$> Brent Lievers <> writes:

    >I have a question about parsing a fixed-width integer from a string.
    >
    >Lines of data are being read from a file having a very strict
    >column-delimited format. In my example below, columns 0-7 are an integer
    >and columns 8-23 are a float. In _most_ files, the first few columns of
    >the float are blank space to make it human readable. But not always.
    >So, once they have been read from the file, I try to parse out the
    >different values using something like the following:
    >
    > #include <stdio.h>
    > #include <stdlib.h>
    >
    > int main()
    > {
    > char line1[]= " 12 15.796473824857";
    > char line2[]= " 1215.7964738248571";
    > int id;
    > float num;
    >
    > sscanf(line1, "%8d%16E", &id, &num);


    "%8d" means: skip any white space characters in the input, then convert
    at most 8 digits. It's obvious that this is not going to work for line2,
    as the conversion will stop at the decimal point.

    To solve the problem, we need two sscanf calls and a char buffer for the
    integer input:

    char buff[8 + 1] = { 0 };

    sscanf(line1, "%8c%16E", buff, &num);
    sscanf(buff, "%d", &id); /* or id = atoi(buff); */

    You may also want to check the return values of the sscanf calls, to be
    sure that the input was OK and you're not propagating garbage into the
    rest of the program.

    Other solutions require an additional statement and destroy a byte of
    input data (can be restored, if necessary), but may have a lower overall
    overhead:

    num = atof(line1 + 8);
    line1[8] = 0;
    id = atoi(line1);

    Dan
    --
    Dan Pop
    DESY Zeuthen, RZ group
    Email:
    Dan Pop, Apr 22, 2004
    #5
  6. Brent Lievers <> wrote:
    > I have a question about parsing a fixed-width integer from a string.


    <snip>

    Thanks to everyone for your responses!

    Brent
    Brent Lievers, Apr 23, 2004
    #6
  7. Brent Lievers

    Dan Pop Guest

    In <> Eric Sosman <> writes:

    > By the way, what's the "%16E" conversion? I've assumed
    >above that it's something akin to "%16e", but if it's more
    >esoteric you may need something fancier than strtod() to
    >imitate it.


    14 The conversion specifiers A, E, F, G, and X are also valid and
    behave the same as, respectively, a, e, f, g, and x.

    or, if you prefer the C89 version:

    The conversion specifiers E, G, and X are also valid and behave
    the same as, respectively, e, g, and x.

    Dan
    --
    Dan Pop
    DESY Zeuthen, RZ group
    Email:
    Dan Pop, Apr 23, 2004
    #7
  8. Brent Lievers

    Eric Sosman Guest

    Dan Pop wrote:
    >
    > In <> Eric Sosman <> writes:
    >
    > > By the way, what's the "%16E" conversion? I've assumed
    > >above that it's something akin to "%16e", but if it's more
    > >esoteric you may need something fancier than strtod() to
    > >imitate it.

    >
    > 14 The conversion specifiers A, E, F, G, and X are also valid and
    > behave the same as, respectively, a, e, f, g, and x.
    >
    > or, if you prefer the C89 version:
    >
    > The conversion specifiers E, G, and X are also valid and behave
    > the same as, respectively, e, g, and x.


    Aha! Thanks for the information. (Why do you suppose
    the fprintf() description lists and describes the upper- and
    lower-case specifiers together, while fscanf() shows only the
    lower-case forms in the big list and relegates the upper-case
    to a separate paragraph? Was the Committee trying to make a
    point about the asymmetry of fprintf() and fscanf()?).

    --
    Eric Sosman, Apr 23, 2004
    #8
  9. Brent Lievers

    Dan Pop Guest

    In <> Eric Sosman <> writes:

    >Dan Pop wrote:
    >>
    >> In <> Eric Sosman <> writes:
    >>
    >> > By the way, what's the "%16E" conversion? I've assumed
    >> >above that it's something akin to "%16e", but if it's more
    >> >esoteric you may need something fancier than strtod() to
    >> >imitate it.

    >>
    >> 14 The conversion specifiers A, E, F, G, and X are also valid and
    >> behave the same as, respectively, a, e, f, g, and x.
    >>
    >> or, if you prefer the C89 version:
    >>
    >> The conversion specifiers E, G, and X are also valid and behave
    >> the same as, respectively, e, g, and x.

    >
    > Aha! Thanks for the information. (Why do you suppose
    >the fprintf() description lists and describes the upper- and
    >lower-case specifiers together, while fscanf() shows only the
    >lower-case forms in the big list and relegates the upper-case
    >to a separate paragraph? Was the Committee trying to make a
    >point about the asymmetry of fprintf() and fscanf()?).


    In fprintf, the behaviour is different. In fscanf, the behaviour is
    identical, so fprintf and fscanf *are* assymetrical :)

    Dan
    --
    Dan Pop
    DESY Zeuthen, RZ group
    Email:
    Dan Pop, Apr 26, 2004
    #9
    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. Replies:
    11
    Views:
    1,080
    Toby Inkster
    Jan 28, 2005
  2. johnp
    Replies:
    4
    Views:
    3,657
    Toby Inkster
    May 23, 2005
  3. ssk
    Replies:
    5
    Views:
    5,468
    Jukka K. Korpela
    Oct 30, 2006
  4. cera
    Replies:
    1
    Views:
    1,250
    Neredbojias
    Aug 18, 2007
  5. Replies:
    1
    Views:
    727
    Beauregard T. Shagnasty
    Dec 29, 2007
Loading...

Share This Page