Scanf/Printf in C...what's going on here? - c

I am self-learning C and I am currently studying the Scanf and Printf functions.
I adapted this program to test myself:
char topping[24];
int slices;
int day, year;
float cost;
char month[10];
printf(" How much does a pizza cost in your area?\n");
printf("$");
scanf(" %f", &cost);
printf("What is your favorite one-word pizza topping?\n");
scanf(" %s",topping);
printf("How many slices of %s pizza, topping can you eat in one sitting\n",topping);
scanf(" %d", &slices);
printf(" What is today's date (enter in the following format 1-Jan-2016 )\n");
scanf(" %d-%s-%d", &day, month, &year);
printf("\nWhy not treat yourself to dinner on %d-%s-%d and have %d slices of %s pizza ? It will only cost you %.2f", day, month, year,slices, topping,cost);
The purpose of the exercise is for me to grasp an understanding of scanf functions and how "delicate" they can be.
My test program here runs well...except for the output in the year variable.
Why is the output in the year variable spitting out gibberish and how can I fix it?
Thank you.

Here's the problem. The %s specifier means to read any characters up until the next whitespace. So , after %d- has matched 1-, then the %s matches Jan-2016. Then the next %d fails as there is nothing left to match.
First of all you should always be checking the return value of scanf so you know whether there was a matching failure. For example in this case:
if ( 3 != scanf(" %d-%s-%d", &day, month, &year) )
{
printf("Matching failure occurred.\n");
// do something else...
}
else
{
printf("Why not treat yourself...
Secondly, to actually avoid the problem. The scanf function is pretty limited as you can see. To use other delimiters instead of whitespace, you can use the scanset specifier %[.....] instead:
scanf(" %d-%9[^-]-%d", &day, month, &year)
(with the same error checking). The scanset specifier means to read any characters (possibly including whitespace) that matches the characters inside the [], except that a ^ indicates to NOT match the next character. So this will read everything up until the next -. Also I added the 9 to avoid overflowing your buffer of size 9+1.
Of course this means if the person never types another - then the program goes a bit weird. You can fix this by making the format string more and more complicated; or you can instead read an entire line with fgets and then use the sscanf function with the aforementioned string. This will catch the case of the person pressing Enter before typing another -.
In fact it turns out it's usually a good idea to read a whole line at a time and then go back and process that line.
At some stage you will get tired of how awful scanf is, and make your own parser.

Related

What format for scanf will read in the example input line?

I'm working on an old C program and ran into a problem with scanf. I have been experimenting for many hours but still cannot get scanf to read a particular line.
The line being read in is:
01/05\t\t840.81\tvisa payment (online)\tAFCU\n
The C code using scanf is:
sscanf(lineIn, "%d/%d %f %[A-Z\(\)a-z0-9]s\t%[A-Za-z0-9]s",
&month, &year, &amt, &item, &supplier);
This line scans the month, year, amt and item variables correctly and the values can be verified. But no matter what format I used at the end, I cannot get the value of supplier to be read in.
Can anyone suggest a format that will scan in all of the variables?
I cannot get the value if of supplier
Scanning stopped on the incorrect s. #William Pursell.
Lack of a ' ' in %[A-Z\(\)a-z0-9] contributes to not scanning all of the item.
Do not use "%s" nor "%[]" without a Width.
Use a width one less than the array count.
s is wrong after a %[]
Drop the s in "%[A-Z\(\)a-z0-9]s". "%[A-Z\(\)a-z0-9]" and "%s" are separate scan directives.
Do not use undefined escapes
\( and and \) are not defined.
float with money is marginally OK
Better as double. Money has a whole hosts of issues too.
Check conversion success
Do not use the address of any array for %[]
Pass the address of the first element of the array.
This implies compiling done without all warnings enabled.
Save time. Enable all warnings.
Allow spaces in item[], supplier[]
... the space in the %100[A-Za-z0-9 ] conversion has a different meaning from the space before %n: a space in a scan set matches the space character whereas a space elsewhere in the format string matches any white space characters, including TAB and newline. #chqrlie
I recommend using " %n" to detect complete conversion success.
Be generous, allow white-space between tokens. %d, %f, %s already allow leading white-space. %[] does not.
int main(void) {
const char *lineIn = "01/05\t\t840.81\tvisa payment (online)\tAFCU\n";
int month, year;
double amt;
char item[90 + 1];
char supplier[100 + 1];
// sscanf(lineIn,"%d/%d %f %[A-Z\(\)a-z0-9]s\t%[A-Za-z0-9]s",
// &month,&year,&amt,&item,&supplier);
int n = 0;
sscanf(lineIn, "%d /%d%lf %90[A-Z()a-z0-9 ] %100[A-Za-z0-9 ] %n", //
&month, &year, &amt, item, supplier, &n);
if (n == 0 || lineIn[n]) {
puts("Failed");
} else {
puts("Success");
}
}

Why is scanf failing to read inputs correctly?

I cant figure out whats wrong. Am i using format specifiers in wrong way? Someone please help i am very new to coding.
#include <stdio.h>
#include <stdlib.h>
int main()
{
char name[20];int age;char grade;double gpa;char area[10];
printf("User Input\n");
printf("Enter your name: ");
fgets(name,20,stdin);
printf("Your name is %s\n",name);
printf("Enter your age: ");
scanf("%d\n",&age);
printf("Your age is %d\n",age);
printf("Enter you grade: ");
scanf("%c\n",&grade);
printf("Your grade is %c\n",grade);//Why is this giving an int output?
printf("Enter your gpa: ");
scanf("%f\n",&gpa);
printf("Your gpa is %f\n",gpa);
printf("Enter your area: ");
scanf("%s\n",&area);
printf("Your area is %s",area);//This shows grade input
return 0;
}
Output
You use fgets correctly when reading name. I'd recommend also using fgets for all your other inputs, and then parsing the intended values out of them. For example:
char age_str[20];
fgets(age_str, 20, stdin);
age = strtol(age_str, NULL, 10);
This is preferable to using scanf directly for non-string inputs since if input fails to match a format string, it will remain in stdin and screw up the other scanf calls.
If you would like to use scanf correctly:
Check its return value to see if it matches the number of format specifiers in the string. If not, some inputs were not successfully read. You may want to use a do/while loop for this.
Begin your format strings with a space, as in " %c", so that any whitespace remaining in stdin will be skipped over.
Don't end your format strings with a newline.
Some things to remember about scanf:
Most conversion specifiers like %s, %d, and %f will skip over leading whitespace - %c and %[ will not. If you want to read the next single non-whitespace character, use " %c" - the leading blank tells scanf skip over any leading whitespace before reading the next non-whitespace character;
For what you are trying to do, you should not use \n in your format strings - it will cause scanf to block until you enter a non-whitespace character;
You do not need to use the & operator on array expressions like area; under most circumstances, array expressions are converted to pointer expressions1. Honestly, you should read area the same way you read name, using fgets (and you should always check the result of fgets), or you should specify the maximum field width in the specifier: scanf( "%9s", area ); (a 10-element array can hold up to a 9-character string, since one element has to be reserved for the string terminator);
You should get in the habit of checking the result of scanf - it will return the number of successful conversions and assignments. For example, scanf( "%d %d", &x, &y ) will return 2 if both x and y are read successfully. It will return EOF if end-of-file is signaled or there's a read error.
scanf will read up to the next character that doesn't match the conversion specifier - IOW, if you're using %d, then scanf will skip over any leading whitespace, then read up to the next character that isn't a decimal digit. That character is left in the input stream. This means if you're using %d and type in 123e456, scanf will read up to that 'e' character and assign 123 to the target. If you try to read again with %d, scanf will immediately stop reading on that e and return 0 without assigning anything to the target (this is called a matching failure). This will continue until you remove that 'e' from the input stream (such as with getchar or fgetc or scanf with the %c specifier, etc.
You need to make sure the types of the arguments match the format specifier. %s expects an argument of type char *, %d expects int *, %f expects float *. %x expects unsigned int *, %lf expects double *, etc.
This is one of the "deeply unintuitive" aspects of C I was talking about in my comment.

Can't understand why I can't loop %[^/n]

I can't seem to find where the problem is in here.
During the first loop, I can enter the name of the creditor but I can't do that during the second loop.
int main(){
float cb,ir,si,sum=0,totaldebt;
int time,i;
char name[25];
printf("------------Welcome to Debt Management System-------------");
for (i=1;i>=1;i++){
printf("\n%d)Name of the creditor: ",i);
scanf("%[^\n]",&name);
printf("Enter your current balance: ");
scanf("%f",&cb);
printf("Enter its interest rate: ");
scanf("%f",&ir);
printf("Enter time for the loan: ");
scanf("%d",&time);
si=cb*ir*time/100;//simple interest
totaldebt=si+cb; //simple interest + current balance
if (name=='none'){
break;
}
sum+=totaldebt;
}
It skip the scanf part and I somewhat guess that the reading part seems stuck by it's previous reading.
To get the effect you seem to be after, %[^\n]*c should be %[^\n]%*c. You need a specifier for the first pattern and then a specifier for a single character. The way you have it written now has you asking scanf to match everything up to a newline, and then read the sequence *c.
It can't read that sequence, but it matches the first specifier. So you end up with an unconsumed newline that is probably tripping your other input.
There's also the potential problem of your for (i=1;i>=1;i++), that condition is fishy, and likely to go on for a while.
Now, while this is all probably a fun exercise, I suggest you ditch scanf and switch to fgets to read lines of input. It's less cryptic, and it forces you to pass a buffer size, which makes using it somewhat easier and safer compared to scanf.
To complete the first answer, I noticed this problem:
if (name=='none')
{
break;
}
This form is not correct in C, you should:
use strcmp
function,
use " instead of ' to define a string:
/* if two strings are the same --but not necessary at the same adress-- strcmp return 0*/
if (0 == strcmp(name, "none"))
{
break;
}
While taking a string input you are not required to use the reference operator '&'. That's why in your first scanf statement use name instead of &name. Also modify the input statement with %*c if you want to take newline character in the input as shown below. Other errors have been stated in the above answers.
scanf("%[^\n]",name); OR
scanf("%[^\n]%*c",name);

How can I make scanf() with multiple inputs ignore the others?

scanf("%s %d %s %d",word1,&num1,word2,&num2);
so when the user inputs "quit", its supposed to stop asking for the other 3 inputs. however it asks me to input another "quit" probably because there are 2 %s in the format
is there anyway around this?
EDIT: because it has to get 4 inputs in a loop, unless a quit is inputted.
scanf is a very blunt tool that is not good at talking to unstructured inputs (including humans :-) ). In general, if you are interacting with a person, you should start with fgets to read a line, then pick the resulting line apart however is most convenient, possibly including sscanf.
It's worse than you think because the %d directive will jam up if you feed it something that is not scan-able as an integer. For instance, if you enter quit now, the first %s directive will read the word quit but the %d will leave now in the input stream, causing scanf to return 1 (one successful conversion-and-assignment). The next attempt to read a string will obtain and consume the now; to naive code, this will seem like it was a later, second input line, rather than a continuation of the first one.
#include <stdio.h>
#include <string.h>
scanf("%s ", word1);
if (strcmp(word1, "quit") != 0)
scanf("%d %s %d", &num1, word2, &num2);

Adding data to arrays in C

I have a few arrays that I wanted to update. The problem is that when data is added to the array, some variables are updated to 0.
my code to add data:
void addStockItem(){
system(CLS_NAME);
printf("New Item\n\n");
printf("New Item Name : ");
fflush(stdin);
scanf(" %[^\n]s",&*itemName[totalItem]);
//itemName[totalItem][30] = strupr(itemName[totalItem]);
fflush(stdin);
printf("New Item Price : ");
scanf("%f",&item_Price_Profit[totalItem][0]);
printf("New Item Profit : ");
scanf("%f",&item_Price_Profit[i][1]);
printf("New Item Qty : ");
scanf("%d",&itemQuantity[totalItem][0]);
itemQuantity[totalItem][1]=0;
itemQuantity[totalItem][2]=0;
++totalItem;
allStocks();
}
the data,
int totalItem=13,itemQuantity[][3]={8,0,0,9,0,0,11,0,0,0,0,0,20,0,0,22,0,\
0,16,0,0,18,0,0,9,0,0,7,0,0,5,0,0,12,0,0,0,0,0},sessionQuantity;
float item_Price_Profit[][2]={1,0.5,2,0.2,3,0.2,4,0.2,5,0.5,6,0.8,7,0.5,8,0.2,9,\
0.2,10,0.2,11,0.5,12,0.8,13,0.9};
char itemName[][30]={"STABILO PENCIL 2B","STABILO PEN 0.5",\
"STABILO ERASER","STABILO RULER","STABILO TEST PAD","STABILO BOOK","STABILO SCISSORS","STABILO SHARPENER","STABILO GLUE","STABILO CHALK","STABILO MARKER PEN","OXFORD DICTIONARY","STABILO HIGHLIGHTER"};
full code: http://pastebin.com/jjuCCrjz
[EDIT]
Everything work as they intended to after I changed itemQuantity[][3] to itemQuantity[100][3], item_Price_Profit[][2] to item_Price_Profit[100][2] and itemName[][30] to itemName[100][30]. What could possibly my mistake other than scanf?
I can't access pastebin from my work computer, so I'll have to go by what's posted.
Several issues off the bat:
fflush is only defined to work on output streams, not input streams; the behavior of fflush(stdin) is undefined (it will do something, but probably not what you want). If you need to clear out garbage from the input stream, you'll need to consume it using getchar() or fgets() or similar until you see a newline or some other indicator that you've cleared out the garbage. The %f and %d conversion specifiers will skip over any leading whitespace, and by having the blank before the %[ conversion specifier will also cause any leading whitespace to be skipped. So ditch the fflush calls altogether.
scanf(" %[^\n]s",&*itemName[totalItem]); - this looks confused. Unless you expect the input to always have a trailing s character, the conversion specifier should simply be %[^\n]. The &* in front of itemName is redundant; you just need to write
scanf(" %[^\n]", itemName[totalItem]);Although you should probably put a field width specifier in there:
scanf(" %30[^\n]", itemName[titalItem]);
to avoid buffer overflow.
You're accessing all your data items (totalItem, itemName, item_Price_Profit, etc.) as global variables. This is usually a recipe for heartburn. Ideally, functions and their callers should not share state via globals; rather, they should communicate through parameters, return values, and exceptions (where supported). Something more like
void addStockItem(char *name, float *price, float *profit, float *quantity)
{
...
scanf(" %30[^\n]", name);
...
scanf("%f", price);
...
scanf("%f", profit);
...
scanf("%d", quantity);
} which would be called like
addStockItem(itemName[totalItem],
&item_Price_Profit[totalItem][0],
&item_Price_Profit[i][1],
&itemQuantity[totalItem][0]);
Your data structures feel really hinky to me, but that's likely because I can't see your entire program.
This:
scanf(" %[^\n]s",&*itemName[totalItem]);
can't be right. This is passing a character converted to a pointer, where scanf() expects a pointer to character. You probably mean:
scanf(" %[^\n]s", itemName[totalItem]);

Resources