strtod с ограниченной длиной строки
Если я хочу проанализировать первые 3 символа из массива char как двойные, игнорируя следующие символы, нужно ли мне это делать?
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)); // печатает 1.20000, чего я и хочу бесплатно (б); }
Разве нет такой функции, как strtod
это позволяет вам указать максимальную длину строки, которую он должен искать цифры?
Изменить: я хочу, чтобы это распечатать 1.2
(что он в настоящее время делает), а не 1.23
!
5 ответов
Если вы всегда хотите рассматривать только три первых символа из данной строки, вы можете использовать следующий код:
#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;
}
В то время как strtod()
не позволяет ограничивать длину строки, вы можете использовать sscanf()
с максимальной шириной поля и необязательной проверкой количества потребляемых символов, например:
#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
является важной частью: вы указываете максимальную ширину 3, что означает, что sscanf()
будет читать до 3 символов для этого конкретного поля, а также будет хранить количество символов, использованных в конце анализа, в numCharsRead
, Затем вы можете проверить это значение, если вам нужно прочитать ровно 3 символа каждый раз; если у вас все в порядке с 3 или меньше, вы можете просто использовать sscanf(str, "%3lf", &val)
, Для справки вот документация для спецификаторов ширины:
Необязательное десятичное целое число, которое определяет максимальную ширину поля. Чтение символов прекращается либо при достижении этого максимума, либо при обнаружении несоответствующего символа, в зависимости от того, что произойдет раньше. Большинство преобразований отбрасывают начальные пробельные символы (исключения отмечены ниже), и эти отброшенные символы не учитываются в максимальной ширине поля. Строковые входные преобразования хранят завершающий нулевой байт ('\0'), чтобы отметить конец ввода; максимальная ширина поля не включает этот терминатор.
Нет, в стандартной библиотеке такой функции нет.
Но весело кататься на своем:
/*
* 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;
}
Подпись strtod
это так
double strtod(const char *nptr, char **endptr);
Функция вернет начальную часть строки, на которую указывает nptr
, Если endptr
не является NULL
указатель на символ после последнего символа, использованного в преобразовании, сохраняется в месте, указанном endptr
,
Поэтому он не позволяет указать количество символов, которые необходимо преобразовать. Следовательно, вы должны изменить свой ввод и передать его strtod
,
Это простая реализация, без которой я работал.malloc
илиstrdup
и другие функции распределения памяти:
double strntod(const char* str, size_t len) {
double d = 0.0, j = 0.0;
size_t i = 0;
for (; i < len; i++) {
char c = str[i];
if (c == '.') { j = 1.0; continue; }
d = d * 10.0 + (double) (c - '0');
if (j > 0.0) j *= 10.0;
}
return d / j;
}
Обратите внимание, что предполагается, что все символы вstr
вплоть доlen
являются числами или точкой, и что в str имеется не более одной точки.