While using strtod()
(for double
) or strtof()
(for float
) is the correct approach for the conversion, and can be used to work through all values in each line read, there actually is a simpler way to approach this problem. Since the sets of ASCII characters (tokens) to be converted to floating-point values are all separated by one or more spaces, you can simply use strtok()
to split the line read into file_string
into tokens and then convert each token with strtod()
(or strtof()
)
What that does is simplify error handling and you having to increment the pointer returned in endptr
past any characters not used in a conversion. If you process the entire line with strtod()
, then for your 12e
or 4e.3
examples of bad values, it would be up to you to check if endptr
pointed to something other than a space (one after the last char used in the conversion), and if that was the case, you would have to manually loop incrementing endptr
checking for the next space.
When you tokenize the line with strtok()
splitting all space separated words in file_string
into tokens, then you only have to worry about validating the conversion and checking if endptr
is pointing at something other than the '\0'
(nul-terminating character) at the end of the token to know if the value was bad.
A quick example of doing it this way could be:
#include <stdio.h>
#include <stdlib.h> /* for strtod() */
#include <string.h> /* for strtok() */
#include <errno.h> /* for errno */
#include <ctype.h> /* for isspace() */
#define DELIM " \t\n" /* delmiters to split line into tokens */
int main (void) {
char file_string[] = "-123.45 1.2e7 boo 1.3e-7 12e 4e.3 1.1 2.22\n",
*nptr = file_string, /* nptr for strtod */
*endptr = nptr; /* endptr for strtod */
size_t n = 0;
nptr = strtok (file_string, DELIM); /* get 1st token */
while (nptr != NULL) {
errno = 0; /* reset errno 0 */
double d = strtod (nptr, &endptr); /* call strtod() */
printf ("\ntoken: '%s'\n", nptr); /* output token (optional) */
/* validate conversion */
if (d == 0 && endptr == nptr) { /* no characters converted */
fprintf (stderr, "error, no characters converted in '%s'.\n", nptr);
d = -1000; /* set bad value */
}
else if (errno) { /* underflow or overflow in conversion */
fprintf (stderr, "error: overflow or underflow converting '%s'.\n",
nptr);
d = -1000; /* set bad value */
}
else if (*endptr) { /* not all characters used in converison */
fprintf (stderr, "error: malformed value '%s'.\n", nptr);
d = -1000; /* set bad value */
}
printf ("double[%2zu]: %g\n", n, d); /* output double */
n += 1; /* increment n */
nptr = strtok (NULL, DELIM); /* get next token */
}
}
(note: the string you pass to strtok()
, e.g. file_string
in your case cannot be a read-only String-Literal because strtok()
modifies the original string. You can make a copy of the string if you need to preserve the original)
Example Use/Output
With file_string
containing "-123.45 1.2e7 boo 1.3e-7 12e 4e.3 1.1 2.22\n"
, you would have:
$ ./bin/strtod_strtok_loop
token: '-123.45'
double[ 0]: -123.45
token: '1.2e7'
double[ 1]: 1.2e+07
token: 'boo'
error, no characters converted in 'boo'.
double[ 2]: -1000
token: '1.3e-7'
double[ 3]: 1.3e-07
token: '12e'
error: malformed value '12e'.
double[ 4]: -1000
token: '4e.3'
error: malformed value '4e.3'.
double[ 5]: -1000
token: '1.1'
double[ 6]: 1.1
token: '2.22'
double[ 7]: 2.22
Look things over and let me know if you have further questions.