Извлечение строки и преобразование в двойной

Я пытаюсь создать функцию, которая может определить, можно ли идеально преобразовать входные данные в двойные, а затем сохранить их в массиве двойных чисел. Например, ввод "12.3a" недопустим. Из того, что я знаю, strtod все еще может конвертировать его в 12.3, а оставшиеся будут сохранены в указателе. В моей функции doubleable она фильтрует, состоит ли входная строка только из цифр. Тем не менее, я знаю, что двойной имеет "." как в "12.3", и моя функция вернет 'X' (недействительно).

#include <stdio.h>
#include <stdlib.h>
#include <string.h>



char doubleable (char unsure[], int length){
int i;
int flag;
for (i=0; i<length; i++ ){
    if(isdigit(unsure[i]==0)){
        printf("You have invalid input.\n");
        flag=1;
        break;
    }
}
//check for '.' (?)
if(flag==1)
    return 'X';
else
    return 'A';



}

int main(){
char input [10];
double converted[5];
char *ptr;
int i;

for(i=0; i<5; i++){
    fgets(input, 10, stdin);
    //some code here to replace '\n' to '\0' in input
    if(doubleable(input, strlen(input))=='X'){ 
        break;
    }

    converted[i]=strtod(input, &ptr);
    //printf("%lf", converted[i]); 
}
    return 0;
} 

Я думаю о чем-то вроде проверки на наличие "." на входе и на сколько (для входов, таких как 12.3.12, которые можно считать недействительными). Я на правильном пути? или есть более простые способы пройти через это? Я также читал о функции strtok, это будет полезно здесь? Эта функция все еще довольно расплывчата для меня.

РЕДАКТИРОВАТЬ:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

double HUGE_VAL= 1000000;

void string_cleaner (char *dirty){
int i=0;
while(1){
    if (dirty[i]=='\n'){
        dirty[i]='\0';
        break;
    }
    i++;
}
}

int doubleable2(const char *str)
{
char *end_ptr;
double result;

result = strtod(str, &end_ptr);

if (result == HUGE_VAL || result == 0 && end_ptr == str)
    return 0;  // Could not be converted

if (end_ptr < str + strlen(str))
    return 0;  // Other input in the string after the number

return 1;  // All of the string is part of the number
}

int main(){
char input [10];
double converted[10];
char *ptr;
int i;

for(i=0; i<5; i++){
    while (1){
    printf("Please enter:");
    fgets(input, 10, stdin);
    string_cleaner(input);
    if (doubleable2(input)==0)
        continue;
    else if (doubleable2(input)==1)
        break;
    }
    converted[i]=strtod(input, &ptr);
    printf("%lf\n", converted[i]);
    }
     return 0;
    }

благодарю вас! Работает просто отлично! У меня есть дополнительный вопрос. Если я введу слишком длинную строку, программа прекратит работу. Если я хочу ограничить ввод, скажем, максимум 9 символами в input[], как мне это сделать?

из того, что я понимаю о fgets(xx, size, stdin), он получает только размер символов (включая \n, \0), а затем сохраняет его в xx. В моей программе я подумал, что если установить 10, то все, что больше 10, не будет рассматриваться. Однако, если я введу слишком длинную строку, моя программа прекратит работу.

3 ответа

Решение

Вы действительно можете использовать strtod и проверьте возвращаемое значение и указатель, данный как второй аргумент:

int doubleable(const char *str)
{
    const char *end_ptr;
    double result;

    result = strtod(str, &end_ptr);

    if (result == HUGE_VAL || result == 0 && end_ptr == str)
        return 0;  // Could not be converted

    if (end_ptr < str + strlen(str))
        return 0;  // Other input in the string after the number

    return 1;  // All of the string is part of the number
}

Обратите внимание, что вам нужно удалить новую строку, которая fgets большую часть времени добавляет в строку перед вызовом этой функции.

Из того, что я знаю, strtod все еще может конвертировать его в 12.3, а оставшиеся будут сохранены в указателе.

Это правильно - смотрите его man страница:

Если endptr не NULL, указатель на символ после последнего символа, использованного в преобразовании, сохраняется в расположении, на которое ссылается endptr.

Так что используйте эту информацию!

#include <stdio.h>
#include <stdbool.h>
#include <errno.h>

bool doable (const char *buf)
{
    char *endptr;

    errno = 0;
    if (!buf || (strtod (buf, &endptr) == 0 && errno))
        return 0;
    if (*endptr)
        return 0;
    return 1;
}

int main (void)
{
    printf ("doable: %d\n", doable ("12.3"));
    printf ("doable: %d\n", doable ("12.3a"));
    printf ("doable: %d\n", doable ("abc"));
    printf ("doable: %d\n", doable (NULL));
    return 0;
}

результаты в

doable: 1
doable: 0
doable: 0
doable: 0

После принять ответ

С помощью strtod() это правильный подход, но у него есть некоторые проблемы

#include <ctype.h>
#include <stdlib.h>

int doubleable3(const char *str) {
  if (str == NULL) {
    return 0;  // Test against NULL if desired.
  }

  char *end_ptr;  // const char *end_ptr here is a problem in C for strtod()
  double result = strtod(str, &end_ptr);

  if (str == end_ptr) {
    return 0;  // No conversion
  }

  // Add this if code should accept trailing white-space like a \n
  while (isspace((unsigned char) *endptr)) {
    endptr++;
  }

  if (*end_ptr) {
    return 0;  // Text after the last converted character
  }

  // Result overflowed or maybe underflowed
  // The underflow case is not defined to set errno - implementation defined.
  // So let code accept all underflow cases
  if (errno) {
    if (fabs(result) == HUGE_VAL) {
      return 0;  // value too large
    }
  }

  return 1; // Success
}

Код ОП

Нет значения с result == 0 в result == 0 && end_ptr == str, Упростить до end_ptr == str,

Вместо if (end_ptr < str + strlen(str)), просто if (*end_ptr) достаточно.

if (result == HUGE_VAL ... это проблема по 2 причинам. 1) Когда result == HUGE_VAL происходит в 2 ситуациях: законное преобразование и преобразование переполнения. Нужно проверить errno сказать разницу. 2) тест должен быть if (fabs(result) == HUGE_VAL ... обрабатывать отрицательные числа.

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