fetching string and converting to double - c

I'm trying to create a function which can determine if an input can be converted perfectly into a double and then be able to store it into an array of doubles. For example, an input of "12.3a" is invalid. From what I know, strtod can still convert it to 12.3 and the remaining will be stored to the pointer. In my function doubleable, it filters whether the input string consists only of digits. However, I'm aware that double has "." like in "12.3", and my function will return 'X' (invalid).
#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;
}
I'm thinking of something like checking for the occurrence of "." in input, and by how much (for inputs like 12.3.12, which can be considered invalid). Am I on the right track? or are there easier ways to get through this? I've also read about the strtok function, will it be helpful here? That function is still quite vague to me, though.
EDIT:
#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;
}
thank you! It works just fine! I have a follow up question. If I enter a string that is too long, the program breaks. If I am to limit the input to, let's say, a maximum of 9 characters in input[], how am I to do that?
from what I understand about fgets(xx, size, stdin), it only gets up to size characters (including \n, \0), and then stores it to xx. In my program, I thought if I set it to 10, anything beyond 10 will not be considered. However, if I input a string that is too long, my program breaks.

You can indeed use strtod and check the returned value and the pointer given as the second argument:
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
}
Note that you need to remove the newline that fgets most of the time adds to the string before calling this function.

From what I know, strtod can still convert it to 12.3 and the remaining will be stored to the pointer.
That is correct – see its man page:
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 use that information!
#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;
}
results in
doable: 1
doable: 0
doable: 0
doable: 0

After accept answer
Using strtod() is the right approach, but it has some challenges
#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
}
OP's code
No value with result == 0 in result == 0 && end_ptr == str. Simplify to end_ptr == str.
Instead of if (end_ptr < str + strlen(str)), a simple if (*end_ptr) is sufficient.
if (result == HUGE_VAL ... is a problem for 2 reasons. 1) When result == HUGE_VAL happens in 2 situations: A legitimate conversion and an overflow conversion. Need to test errno to tell the difference. 2) the test should be if (fabs(result) == HUGE_VAL ... to handle negative numbers.

Related

How to join two numbers of type char and make them int type

Essentially I have a line which ends in two numbers. I can read the numbers eg) '4' and '1'. I want to concatenate them into '41' and then read that as an int type of value 41. Converting a single character to int is straight forward but how would this work for two (or more) characters?
I am grabbing the characters using:
int first_digit = ctoi(line[1]);
int second_digit = ctoi(line[2]);
where ctoi is defined as :
int ctoi( int c ) // https://stackoverflow.com/a/2279401/12229659
{
return c - '0';
}
Easiest way would be to use a function such as sscanf (provided that line is a proper string)
int num;
if (sscanf(line, "%d", &num) != 1) {
// handle conversion error
}
Although, scanf in general doesn't provide protection from arithmetic overflow, so for a big number it will fail (and you won't be able to track it).
strtol and friends, will fail (and will let you know) when you exceed the range.
You could however build your own function, again with no overflow protection:
#include <ctype.h>
#include <stdlib.h>
int stringToInt(char *str) {
int num = 0;
size_t start = (*str == '-') ? 1 : 0; // handle negative numbers
for (size_t i = start; str[i] != '\0'; i++) {
if (isdigit((unsigned char)str[i]) == 0) { // we have a non-digit
exit(1); // ideally you should set errno to EINVAL and return or terminate
}
num = (num * 10) + (str[i] - '0');
}
return (start) ? -num : num;
}

Why is my program counting letters and digits not running?

I wrote this function tat checks for upper, lower and digits in a string but when I'm trying to run the code this pops up and cant seem to understand the problem.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define size 50
void statistics(char str[], int *lower, int *upper, int *digits) {
for (int i = 0; i < size; i++) {
if (islower(str[i]) != 0) {
*lower = *lower + 1;
} else
if (isupper(str[i]) != 0) {
*upper = *upper + 1;
} else
if (isalpha(str[i])) {
*digits = *digits + 1;
}
}
}
int main() {
char str[size] = { " " };
int upper = 0, lower = 0, digits = 0;
printf("Enter a string:\n");
gets_s(str);
statistics(&str[size], &lower, &upper, &digits);
printf("Lower: %d\nUpper: %d\nDigits %d", lower, upper, digits);
return 0;
}
There are multiple problems in your code:
the gets_s() function is not portable: it is optional and not supported on many systems. You forget to pass the array size, hence causing undefined behavior. The compiler should output a diagnostic that you should not ignore. You should use fgets() instead.
you should not pass char values to the isupper() and similar functions because they are only defined for values of the type unsigned char and the special negative value EOF. Use an unsigned char variable or cast the str[i] argument as (unsigned char)str[i].
you pass the address of the end of the char array instead of the beginning. Just pass str as the argument to statistics. The statistics function reads characters beyond the end of the array, invoking undefined behavior, and one of these bytes happens to be a negative char value less than -1 triggering the diagnostic in your Visual C++ compiler runtime. The error message is difficult to interpret, the IDE should point you to the calling code.
you iterate on the whole array, beyond the null terminator. The contents of the array is undefined beyond the null terminator set by gets_s() or fgets(). Just stop at the null terminator.
you test if (isalpha(ch)) where you probably mean to use if (isdigit(ch))
the isxxx functions return a non zero value for true and zero for false. It is idiomatic in C to just write if (isdigit(c)) instead of if (isdigit(c) != 0) which seems redundant.
defining size as a macro is error prone. Use upper case and a more explicit name.
Here is a modified version:
#include <ctype.h>
#include <stdio.h>
#define LINE_SIZE 50
void statistics(const char *str, int *lower, int *upper, int *digits) {
while (*str != '\0') {
unsigned char ch = *str++;
if (islower(ch)) {
*lower += 1;
} else
if (isupper(ch)) {
*upper += 1;
} else
if (isdigit(ch) {
*digits += 1;
}
}
}
int main() {
char str[LINE_SIZE];
int upper = 0, lower = 0, digits = 0;
printf("Enter a string:\n");
if (fgets(str, sizeof str, stdin)) {
statistics(str, &lower, &upper, &digits);
printf("Lower: %d\nUpper: %d\nDigits %d", lower, upper, digits);
}
return 0;
}

Check if a number is an integer or not in C language

I want to check if a number given by a user is an integer or not in another way i want to verify if the input data is between −(2)^31= −2,147,483,648 and ((2)^31) - 1 =2,147,483,647
this is my code:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main()
{
int x;
int y = pow(3,31) * (-1);
int z = pow(3,32) - 1;
printf("\n\ty = %d et z = %d \n\n", y, z);
scanf("%d", &x);
if ((x < y) || (x > z)) {
printf("x is not an integer");
}
else {
printf("x is an integer");
}
return 0;
}
But while running the program the result always showing me x is integer even if x is greater than 2,147,483,647 or lesser than −2,147,483,648.
Testing whether input is a valid int decimal numeral or is a decimal numeral in [-231, 231) is actually a bit complicated. The C standard does not provide a direct way to do this. What we can do is:
Read characters and check to see whether they are in the expected form: spaces, an optional minus sign (hyphen), and digits. (Any non-digits after the digits will be allowed and ignored.)
Try using strtol to convert the numeral to a long. We use strtol because there is no C-standard library routine for converting to an int (or your fixed bounds using 231) that provides error indications.
Compare the long produced by strtol to the int bounds.
Example code for int bounds follows. If you want bounds of -2147483648 and 2147483647 instead, substitute those for INT_MIN and INT_MAX. To be completely safe, the code should actually use long long and strtoll, since the C standard does not require long to be able to represent −2147483648.
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
// Prepare a buffer.
size_t BufferSize = 100, BufferUsed = 0;
char *Buffer = malloc(BufferSize * sizeof *Buffer);
// Skip white space.
int c;
do
c = getchar();
while (isspace(c));
if (c == EOF)
{
printf("Input is not an int: EOF before \"-\" or digit seen.\n");
exit(EXIT_SUCCESS);
}
// Accept a hyphen as a minus sign.
if (c == '-')
{
Buffer[BufferUsed++] = c;
c = getchar();
}
// Accept digits.
while (isdigit(c))
{
Buffer[BufferUsed++] = c;
if (BufferSize <= BufferUsed)
{
BufferSize *= 2;
printf("Realloc: size = %zu, used = %zu.\n", BufferSize, BufferUsed);
char *NewBuffer = realloc(Buffer, BufferSize * sizeof *NewBuffer);
if (!NewBuffer)
{
fprintf(stderr, "Error, unable to allocate %zu bytes.\n",
BufferSize);
exit(EXIT_FAILURE);
}
Buffer = NewBuffer;
}
c = getchar();
}
// Ensure we saw at least one digit (input is not blank or just a hyphen).
if (BufferUsed == 0 || BufferUsed == 1 && Buffer[0] == '-')
{
printf("Input is not an int: No digits present.\n");
exit(EXIT_SUCCESS);
}
// Put back the unaccepted character, if any.
if (c != EOF)
ungetc(c, stdin);
// Terminate the string.
Buffer[BufferUsed] = 0;
// Attempt to convert the numeral to long.
char *End;
errno = 0;
long x = strtol(Buffer, &End, 10);
// Test whether strtol succeeded.
if (*End)
{
/* I do not expect this to occur since we already tested the input
characters.
*/
printf("Input is not an int: strtol rejected %c.\n", *End);
exit(EXIT_SUCCESS);
}
if (errno == ERANGE)
{
printf("Input is not an int: strtol reported out of range.\n");
exit(EXIT_SUCCESS);
}
if (x < INT_MIN || INT_MAX < x)
{
printf("Input is not an int: Value is outside bounds.\n");
exit(EXIT_SUCCESS);
}
printf("Input is an int, %ld.\n", x);
free(Buffer);
}
Maybe i think i should store the number on a char array and check if it contains the float character '.'
#include<stdio.h>
#include<string.h>
#include <stdlib.h>
int main(){
char number[10];
int flag=0,i = 0;
printf("\n\nEnter a number: ");
scanf("%s", number);
while(number[i++] != '\0'){
if(number[i] == '.'){
flag = 1;
break;}}
if(flag)
printf("\n\n\n\tyou Entered a Floating Number not an integer number\n\n");
else
printf("\n\n\n\t you Entered an integer Number\n\n");
return 0;}

Code Blocks error when using while(scanf) in C

I have attached a piece of code below which works perfectly fine in an online compiler but fails to work in Code Blocks compiler when using C. I have attached screenshots as well.
#include <stdio.h>
int main() {
int i = 0;
int array[100];
while(scanf("%d",&array[i])>0)
{
i++;
}
for(int j=0;j<i;j++)
{
printf("%d ",array[j]);
}
return 0;
}
Using Online compiler(GeeksForGeeks)
Using CODEBLOCKS compiler
There is no error, your while loop will go on until an invalid input is entered, you have no limit for the number of inputs so it will continue taking values, which may later become a problem since your container only has space for 100 ints.
It stops on some online compilers because of the way they use stdin inputs, it's basically a one time readout.
Examples:
It stops here, has one time stdin readout.
It doesn't stop here, has a console like input/output.
So if you want to stop at a given number of inputs you can do something like:
//...
while (i < 5 && scanf(" %d", &array[i]) > 0)
{
i++;
}
//...
This will read 5 ints, exit the loop and continue to the next statement.
If you don't really know the number of inputs, you can do something like:
//...
while (i < 100 && scanf("%d", &array[i]) > 0) { // still need to limit the input to the
// size of the container, in this case 100
i++;
if (getchar() == '\n') { // if the character is a newline break te cycle
// note that there cannot be spaces after the last number
break;
}
}
//...
The previous version lacks some error checks so for a more comprehensive approach you can do somenthing like this:
#include <stdio.h>
#include <string.h> // strcspn
#include <stdlib.h> // strtol
#include <errno.h> // errno
#include <limits.h> // INT_MAX
int main() {
char buf[1200]; // to hold the max number of ints
int array[100];
char *ptr; // to iterate through the string
char *endptr; // for strtol, points to the next char after parsed value
long temp; //to hold temporarily the parsed value
int i = 0;
if (!fgets(buf, sizeof(buf), stdin)) { //check input errors
fprintf(stderr, "Input error");
}
ptr = buf; // assing pointer to the beginning of the char array
while (i < 100 && (temp = strtol(ptr, &endptr, 10)) && temp <= INT_MAX
&& errno != ERANGE && (*endptr == ' ' || *endptr == '\n')) {
array[i++] = temp; //if value passes checks add to array
ptr += strcspn(ptr, " ") + 1; // jump to next number
}
for (int j = 0; j < i; j++) { //print the array
printf("%d ", array[j]);
}
return EXIT_SUCCESS;
}

BUG IN DEVC++ (Comparing String and Character)

I'm trying to make a program that will count how many letters occur in my string. I want only to count letters A and B on a give string.
char string[10];
int countA, countB;
gets(string);
for(int i = 0; i <strlen(string); i++){
if(string[i] == 'A')
countA++;
else if(string[i] == 'B')
countB++;
}
printf("%d %d", countA, countB);
return 0;
for example my input is: ABABA
the output should be 3 2 however it print a different answer for countB. I'm using devc++. Is this a bug?
Reason for getting different result:
Earlier when you didn't initialize the variable countA and countB they contained indeterminate value. Using them in your code introduces undefined behavior.
Two points:
Intialize the variables to zero. countA and countB.
And don't use gets rather use fgets.
I am giving you an example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(){
char string[10];
unsigned int countA=0, countB=0;
if( fgets(string,10,stdin) == NULL){
fprintf(stderr, "%s\n", "Error in string input");
exit(1);
}
size_t len = strlen(string);
if( len > 0 )
string[len-1]='\0';
for(size_t i = 0; i <strlen(string); i++){
if(string[i] == 'A'){
countA++;
}
else if(string[i] == 'B'){
countB++;
}
}
printf("%u %u", countA, countB);
return EXIT_SUCCESS;
}
Note:
Also you are asked whether it is gloabal variable. If it was then probably you wouldn't have to worry about initialization. They would be initialized with 0.
gets() goes on reading characters until it encounters \n or EOF. And on doing this it is not constrained in anyway with the buffer size, leaving a chance of buffer overflow.

Resources