sscanf fixed-width integer question

B

Brent Lievers

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
 
K

- Kees van der Bent -

Brent said:
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
 
K

- Kees van der Bent -

- Kees van der Bent - said:
Brent said:
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
 
E

Eric Sosman

Brent said:
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.
 
D

Dan Pop

In said:
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
 
D

Dan Pop

In said:
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
 
E

Eric Sosman

Dan said:
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()?).
 
D

Dan Pop

In said:
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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,066
Latest member
VytoKetoReviews

Latest Threads

Top