问题
If I want to parse the first 3 characters from the char array as a double, ignoring the following characters, do I really need to do this?
int main() { const char a[] = "1.23"; char *b = malloc(sizeof(char) * 4); memcpy(b, a, sizeof(char) * 3); b[3] = '\0'; printf("%f\n", strtod(b, NULL)); // Prints 1.20000, which is what I want free(b); }
Isn't there a function like strtod
that allows you to specify the maximum string length it should be searching for digits?
Edit: I want it to print 1.2
(which it currently does), not 1.23
!
回答1:
If you always want to only consider the three first characters from a given string, you can use the following code:
#include <stdio.h>
#include <string.h>
double parse_double(const char *str) {
char *tmp = 0;
double result = 0;
asprintf(&tmp, "%.3s", str);
result = strtod(tmp, 0);
free(tmp);
return result;
}
int main(void) {
printf("%f\n", parse_double("1.23")); // 1.2
printf("%f\n", parse_double("1234")); // 123
printf("%f\n", parse_double("0.09")); // 0.0
return 0;
}
回答2:
While strtod()
doesn't allow you to limit the string length, you can use sscanf()
with a maximum field width and an optional check for the number of characters consumed, like so:
#include <stdio.h>
double parseDouble(const char *str){
double val = 0;
int numCharsRead;
// Handle errors by setting or returning an error flag.
if(sscanf(str, "%3lf%n", &val, &numCharsRead) != 1){
puts("Failed to parse double!");
}
else if(numCharsRead != 3){
puts("Read less than three characters!");
}
return val;
}
int main(){
printf("%lf\n", parseDouble("1.3")); // 1.300000
printf("%lf\n", parseDouble("1.5999")); // 1.500000
printf("%lf\n", parseDouble(".391")); // 0.390000
printf("%lf\n", parseDouble(".3")); // Read less than three characters!\n0.300000
return 0;
}
sscanf(str, "%3lf%n", &val, &numCharsRead
is the important part: you specify a maximum width of 3, which means that sscanf()
will read up to 3 characters for that particular field, and also store the number of characters consumed by the end of the parse in numCharsRead
. You can then check that value if you care about reading exactly 3 characters every time; if you're fine with 3 or less, you can just use sscanf(str, "%3lf", &val)
. For reference, here's the documentation for width specifiers:
An optional decimal integer which specifies the maximum field width. Reading of characters stops either when this maximum is reached or when a nonmatching character is found, whichever happens first. Most conversions discard ini‐ tial white space characters (the exceptions are noted below), and these discarded characters don't count toward the maximum field width. String input conversions store a terminating null byte ('\0') to mark the end of the input; the maximum field width does not include this terminator.
回答3:
The signature of strtod
is like this
double strtod(const char *nptr, char **endptr);
The function will return the initial portion of the string pointed to by nptr
. If endptr
is not NULL
, a pointer to the character after the last character used in the conversion is stored in the location referenced by endptr
.
So it does not let you specify the number of characters that need to be converted. Hence you have to modify your input itself and pass it to strtod
.
回答4:
No, there isn't such a function in the standard library.
But it's fun to roll one's own:
/*
* Same as strtod() but only takes the first n characters into account.
* Additionally returns 0. and sets errno to EINVAL if 'nptr' is NULL.
*/
double strntod(const char *nptr, char **endptr, size_t n)
{
double result;
/* perform input validation */
if (!nptr)
{
errno = EINVAL;
result = 0.;
if (endptr)
{
*endptr = nptr;
}
goto lblExit;
}
if (strlen(nptr) <= n)
{
/* Nothing to truncate: fall back to standard 'strtod()' */
result = strtod(nptr, endptr);
}
else
{
/* create working copy of string */
char * ptmp = strdup(nptr);
/* Test whether 'strdup()' failed */
if (!ptmp)
{
result = 0.;
if (endptr)
{
*endptr = nptr;
}
goto lblExit;
}
/* truncate working copy to n characters */
ptmp[n] = '\0';
/* do original 'strtod()' on truncated working copy */
result = strtod(ptmp, endptr);
/* adjust '*endptr' to point to original character array, but to working copy */
if (endptr)
{
*endptr = nptr + (*endptr - ptmp);
}
/* free working copy */
free(ptmp);
}
lblExit:
return result;
}
来源:https://stackoverflow.com/questions/16412840/strtod-with-limited-string-length