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 имеется не более одной точки.

Другие вопросы по тегам