understanding ATOI function - c

Hi I have a text file which contains the below data
ABC00011234567
XYZ00021234567
To get the data, i have defined a structure
typedef struct data {
char x[3];
char y[4];
char z[7];
} key;
in the program what I do is read each line and assign it to the structure
unsigned char buf[1024];
fgets(buf,sizeof(buf),fptr);
key *k=(key*)buf;
int y = atoi(k->y)
printf( "y=%d\n",y);`
I'm getting the output as
y=1123456
y=2123456
the output Im expecting is
y=1
y=2
should I assume, atoi takes the pointer of the string and iterates till EOF is encountered?
what should I do to get the values 1 and 2?

atoi takes a nul-terminated string. You'll have to add your own terminators to your key members if you want to limit the length of data atoi parses

You should assume that atoi() keeps going until it reaches the end of the string or an invalid character. For example, for the string `"123zzz" it'd return 123.
You should either terminate your strings (put a zero at the end of them) and stop using atoi() (e.g. use strtol() instead); or write your own conversion that doesn't need a terminated string.
Note: (in general) atoi() should never be used for anything other than writing a compiler, because it does things that don't make sense to normal people (e.g. "0129" is 10 and not 129 because it decides the number is octal and the 9 isn't a valid digit for octal).

The atoi function expects a null-terminated string; you are passing a portion of the char array that has its termination past the boundaries of key::y, so atoi interprets the entire value as a number. If you would like to stick to your "cookie cutter" method of parsing the key, you need to make a copy, and pass it to atoi:
char temp[5];
memcpy(temp, k->y, 4);
temp[4] = '\0';
int y = atoi(temp);
However, I think that using fscanf is a better choice:
char x[4];
int y, z;
fscanf(fptr, "%3s%4d%7d", x, &y, &z);
printf("%s %d %d", x, y, z);

atoi() assumes a zero terminated string. In your case, the string will not be zero-terminated, and thus the data in z is read by atoi after y has been read.
To read just the 4 digits, you can use sscanf:
sscanf(k->y,"%4d",&y);

Related

Why am I not getting the concatenated string?

I have written this code where I want to add two integers, two doubles and concatenate two strings out of which one of the integer, double and the string is already declared and the other integer, string and double are to be taken by the user. But it seems that the program isn't taking another string as an input.
I have written a similar program where I can take the string from the user using scanf but the same isn't working here.
int main() {
int i = 4;
double d = 4.0;
char s[] = "My college name is ";
// Declare second integer, double, and String variables.
int i2,sum1;
double d2,sum2;
char s2[100];
// Read and save an integer, double, and String to your variables.
scanf("%d",&i2);
scanf("%lf",&d2);
scanf("%[^\n]%*c",&s2);
sum1= i+i2;
sum2= d+d2;
strcat(s,s2);
// Print the sum of both integer variables on a new line.
printf("%d\n",sum1);
printf("%.1lf\n",sum2);
printf("%s",s);
return 0;}
After I made the necessary changes like removing & from s2 and changing s[] to s[200], I still cannot get the concatenated string. I am writing my edited code. Kindly help me with that.
int main() {
int i = 4;
double d = 4.0;
char s[200] = "My college name is ";
// Declare second integer, double, and String variables.
int i2,sum1;
double d2,sum2;
char s2[100];
// Read and save an integer, double, and String to your variables.
scanf("%d",&i2);
scanf("%lf",&d2);
scanf("% [^\n]%*c",s2);
sum1= i+i2;
sum2= d+d2;
strcat(s,s2);
// Print the sum of both integer variables on a new line.
printf("%d\n",sum1);
printf("%.1lf\n",sum2);
printf("%s",s);
return 0;
}
Kindly help me with the bug here.
It's not taking your string input because you use %[^\n]%*c to scan the string. which instuct the program to return after geting a newline as input. And the string got a newline form the buffer after scanning d2, and return with out taking further input.
To get rid of this you need to input a char before taking the input of the string. Change the following lines:
scanf("%lf",&d2);
scanf("%[^\n]%*c",&s2);
To:
scanf("%lf",&d2);
getchar();
scanf("%[^\n]%*c",&s2);
And your code will take the string input properly.
Additionally, you can also do this (taking a extra character input befor string input) by putting a extra space before % sign.
Changing the following line:
scanf("%[^\n]%*c",&s2);
To:
scanf(" %[^\n]%*c",&s2);
Also do the same thing.
You are passing the wrong type of argument to scanf. s2 is an array of chars, so &s2 is a pointer to an array of chars, not a pointer to a char.
(You also ought to have bounds checking to prevent array overflows, add a newline to your final printf, etc. But eliminating the & will make your program compile and run)
Possibly your use of
scanf("%[^\n]%*c",&s2);
As far as I'm aware you can use
scanf("%[^\n]%*c",s2);
or
scanf("%[^\n]%*c",&s2[0]);
As the variable s2 is itself a pointer to the first memory address of the array, using &s2 is just a pointer to a pointer and has no allocated consecutive memory addresses to fill. Hope this helps.
Replace:
scanf("%[^\n]%*c",&s2);
With:
fgetc(stdin);
fgets(s2, 100,stdin);

atoi ignores a letter in the string to convert

I'm using atoi to convert a string integer value into integer.
But first I wanted to test different cases of the function so I have used the following code
#include <stdio.h>
int main(void)
{
char *a ="01e";
char *b = "0e1";
char *c= "e01";
int e=0,f=0,g=0;
e=atoi(a);
f=atoi(b);
g=atoi(c);
printf("e= %d f= %d g=%d ",e,f,g);
return 0;
}
this code returns e= 1 f= 0 g=0
I don't get why it returns 1 for "01e"
that's because atoi is an unsafe and obsolete function to parse integers.
It parses & stops when a non-digit is encountered, even if the text is globally not a number.
If the first encountered char is not a space or a digit (or a plus/minus sign), it just returns 0
Good luck figuring out if user input is valid with those (at least scanf-type functions are able to return 0 or 1 whether the string cannot be parsed at all as an integer, even if they have the same behaviour with strings starting with integers) ...
It's safer to use functions such as strtol which checks that the whole string is a number, and are even able to tell you from which character it is invalid when parsing with the proper options set.
Example of usage:
const char *string_as_number = "01e";
char *temp;
long value = strtol(string_as_number,&temp,10); // using base 10
if (temp != string_as_number && *temp == '\0')
{
// okay, string is not empty (or not only spaces) & properly parsed till the end as an integer number: we can trust "value"
}
else
{
printf("Cannot parse string: junk chars found at %s\n",temp);
}
You are missing an opportunity: Write your own atoi. Call it Input2Integer or something other than atoi.
int Input2Integer( Str )
Note, you have a pointer to a string and you will need to establish when to start, how to calculate the result and when to end.
First: Set return value to zero.
Second: Loop over string while it is not null '\0'.
Third: return when the input character is not a valid digit.
Fourth: modify the return value based on the valid input character.
Then come back and explain why atoi works the way it does. You will learn. We will smile.

C Data Types reading different input from stdin and printing altered data

Dusting off my C cobwebs here using a site called HackerRank... the challenge here is to read 3 different inputs from stdin and then print out altered data.
Input
The first line contains an integer
The second line contains a double
The third line contains a string / sentence
Output
integer input + variable i
double input + variable d
variable s + string input
Seemed pretty straight forward, I'd use scanf for the integer and double then fgets for the string since scanf would terminate after the first space.
My problem is, doesn't seem like fgets is filling the buffer, but I'm unsure whether or not it could be the sites compiler or just my lack of knowledge.
int i = 4;
double d = 4.0;
char s[] = "HackerRank ";
// Declare second integer, double, and String variables.
int singleNum;
double doubleNum;
char buffer[256];
char outputString[300];
// Read and save an integer, double, and String to your variables.
scanf("%d", &singleNum);
scanf("%lf", &doubleNum);
fgets(buffer, 256, stdin);
// Print the sum of both integer variables on a new line.
singleNum += i;
printf("%d\n", singleNum);
// Print the sum of the double variables on a new line.
doubleNum += d;
printf("%.1f\n", doubleNum);
// Concatenate and print the String variables on a new line
strcat(outputString, s);
strcat(outputString, buffer);
printf("%s", outputString);
// The 's' variable above should be printed first.
However, when I do this, buffer is always empty. If I were to use scanf I would at least get the first word front the string input.
Not super concerned about memory usage here, just trying to complete the problem to work within fixed parameters.
So, my question is - am I doing something wrong here?
My Output:
Input (stdin)
12
4.0
is the best place to learn and practice coding!
Your Output (stdout)
16
8.0
HackerRank
Expected Output
16
8.0
HackerRank is the best place to learn and practice coding!
Compiler Message
Wrong Answer
However, when I do this, buffer is always empty. If I were to use scanf I would at least get the first word front the string input.
The problem is that white space ('\n' entered at the end of scanning double number ) into the buffer is getting consumed
instead consume white space using scanf(" "); before scanning in buffer
scanf(" ");
fgets(buffer, 256, stdin);
Is there a way to include the newline character in the scanf statement so I don't need an extra one?
yes you can further simplify above two statements into :
scanf(" %255[^\n]",buffer); //consumes and scans into buffer
or you could also :
scanf("%lf\n", &doubleNum); //consume at the end
fgets(buffer, 256, stdin); //scan into buffer
One visible problem here is that the outputString is declared, but not initialized:
char outputString[300];
I assume, that it is declared in block scope, so it contains trash values, whatever is on the stack. This may confuse strcat, which expects it to be NUL terminated:
strcat(outputString, s);
The fix would be add following line before the strcat call:
outputString[0] = '\0';
I have solved the Hackerrank C dataType First day challenge problem without using strcat function. Below is the solution to this problem.
int main() {
int i = 4;
double d = 4.0;
char s[] = "HackerRank ";
// Declare second integer, double, and String variables.`enter code here`
int i1=0;
double d1=0.0;
char name[100];
// Read and save an integer, double, and String to your variables.
scanf("%d",&i1);
scanf("%lf",&d1);
getchar(); // Used this function to remove the '\n'from double.
scanf("%[^\n]s",name);
// Print the sum of both integer variables on a new line.
printf("%d\n",i+i1);
// Print the sum of the double variables on a new line.
printf("%0.1lf\n",d+d1);
// Concatenate and print the String variables on a new line
// The 's' variable above should be printed first.
printf("%s%s\n",s,name);
return 0;
}

C dynamic data specifiers

I want to write something like the following code, but I failed.
I want to assign dynamic number of chars from array y to x. This number will be defined later. Here is a simple example of what I mean.
#include<stdio.h>
int main()
{
int x=0;
char y[]={'5','4'};
int z=1;
sscanf(y,"%zi",&x);
printf("%i",x); //Each time value of x=0
sscanf(y,"%1i",&x); //I want to make this 1 dynamic "int z"
printf("%i",x); //Here x value =54.
return 0;
}
Succinctly, you can't do what you want directly.
With printf(), you can use this, where all three variables are of type int and the widths will be read from x and y:
printf("%*.*d", x, y, z);
However, the scanf() family of functions provides nothing analogous. A * means 'assignment suppression' in scanf(). Note that in C99 and beyond, %zi tells scanf() that the type of the pointer argument is size_t *. This probably accounts for why you got 0 as a result; sscanf() was writing out of bounds for the variable you passed to it.
Your best bet is to use snprintf() to create the format string you want used by scanf().
int x;
char y[] = { '5', '4' };
int z = 1;
char format[16];
snprintf(format, sizeof(format), "%%%di", z);
if (sscanf(y, format, &x) != 1)
…handle error…
printf("%d\n", x); // Will print 5
Note that if z is 1 or 2, this works OK; if z is larger, then y is not a null-terminated string and you run into undefined behaviour. If z is zero or negative, you run into problems too.
If you see , you declared and initialized array like this -
char y[]={'5','4'};
y is not a string because it is not terminated using '\0' , so you need to explicitly add nul terminater . As sscanf will take a c-style string as first argument.
char y[]={'5','4','\0'}; // or char y[]="54";
Working demo

Defining value of scanf string letters to be read with a variable value

With printf it is perfectly normal to do:
int dec = 3;
float n = 4.3232;
printf("%.*f", dec, n);
But in scanf() I want to replace 100
scanf(%100[^~], string)
with something like:
int a = 100;
scanf(%[***somtehing goes here***][^~], a, string);
But I didn't manage to do it.
Not sure if it is duplicate, I will delete the question if it is.
Edit: replaced '\n' with ~.
For your stated purpose it's probably better to do this:
fgets(string, a, stdin);
http://linux.die.net/man/3/fgets
Just do a first "pass" using sprintf() where you construct the format string that you then use with scanf():
char fmt[64];
const int a = 100;
sprintf(fmt, "%%%d[^\n]", a);
The first two % signs are parsed as a unit by sprintf(); they cause it to emit a single % into the destination string.
The second %d is just the regular code to format a (decimal) integer, it will emit 100.
So the result will be that fmt contains the string "%100[^\n]" (where the \n really means an embedded newline).
Then use fmt with scanf():
const int got = scanf(fmt, string);
As usual, be sure to check the value of got after the call, if it's not 1 then that means scanf() failed to do the requested conversion.

Resources