Извлечение строки и преобразование в двойной
Я пытаюсь создать функцию, которая может определить, можно ли идеально преобразовать входные данные в двойные, а затем сохранить их в массиве двойных чисел. Например, ввод "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 ...
обрабатывать отрицательные числа.