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

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);

Related

C skips user input from subsequent scanf statement [duplicate]

I've been having a lot of problems trying to figure out how to use scanf(). It seems to work fine with integers, being fairly straight forward scanf("%d", &i).
Where I am running into issues is using scanf() in loops trying to read input. For example:
do {
printf("counter: %d: ", counter);
scanf("%c %c%d", &command, &prefix, &input);
} while (command != 'q');
When I enter in a validly structured input like c P101, it seems to loop again before prompting me. This seems to happen even with a single:
scanf("%c", &c)
in a while loop. It'll do the loop twice before prompting me again. What is making it loop twice, and how do I stop it?
When I enter in less amount of input that programmatically wouldn't have another character or number such as q, pressing enter seems to prompt me to enter more. How do I get scanf() to process both single and double character entries?
When you enter "c P101" the program actually receives "c P101\n". Most of the conversion specifiers skip leading whitespace including newlines but %c does not. The first time around everything up til the "\n" is read, the second time around the "\n" is read into command, "c" is read into prefix, and "P" is left which is not a number so the conversion fails and "P101\n" is left on the stream. The next time "P" is stored into command, "1" is stored into prefix, and 1 (from the remaining "01") is stored into input with the "\n" still on the stream for next time. You can fix this issue by putting a space at the beginning of the format string which will skip any leading whitespace including newlines.
A similiar thing is happening for the second case, when you enter "q", "q\n" is entered into the stream, the first time around the "q" is read, the second time the "\n" is read, only on the third call is the second "q" read, you can avoid the problem again by adding a space character at the beginning of the format string.
A better way to do this would be to use something like fgets() to process a line at a time and then use sscanf() to do the parsing.
It's really broken! I didn't know it
#include <stdio.h>
int main(void)
{
int counter = 1;
char command, prefix;
int input;
do
{
printf("counter: %d: ", counter);
scanf("%c %c%d", &command, &prefix, &input);
printf("---%c %c%d---\n", command, prefix, input);
counter++;
} while (command != 'q');
}
counter: 1: a b1
---a b1---
counter: 2: c d2
---
c1---
counter: 3: e f3
---d 21---
counter: 4: ---e f3---
counter: 5: g h4
---
g3---
The output seems to fit with Robert's answer.
Once you have the string that contains the line. i.e. "C P101", you can use the parsing abilities of sscanf.
See:
http://www.cplusplus.com/reference/clibrary/cstdio/sscanf.html
For question 1, I suspect that you've got a problem with your printf(), since there is no terminating "\n".
The default behavior of printf is to buffer output until it has a complete line. That is unless you explicitly change the buffering on stdout.
For question 2, you've just hit one of the biggest problems with scanf(). Unless your input exactly matches the scan string that you've specified, your results are going to be nothing like what you expect.
If you've got an option you'll have better results (and fewer security issues) by ignoring scanf() and doing your own parsing. For example, use fgets() to read an entire line into a string, and then process the individual fields of the string — maybe even using sscanf().
Perhaps using a while loop, not a do...while loop will help. This way the condition is tested before execution of the code.
Try the following code snippet:
while(command != 'q')
{
//statements
}
Also, if you know the string length ahead of time, 'for' loops can be much easier to work with than 'while' loops. There are also some trickier ways to dynamically determine the length as well.
As a final rant: scanf() does not "suck." It does what it does and that is all.
The gets() function is very dangerous (though convenient for no-risk applications), since it does not natively do any checking of the input. It is VERY commonly known as a point of exploit, specifically buffer overflow attacks, overwriting space in registers not allocated for that variable. Therefore if you choose to use it, spend some time putting some solid error checking/correction in.
However, almost invariably, either fgets() or POSIX getline() should be used to read the line — noting that the functions both include the newline in the input string, unlike gets(). You can remove the trailing newline from string read by either fgets() or getline() using string[strcspn(string, "\n")] = '\0'; — this works reliably.

Whats wrong with my SIMPLE C program?

I am writing a super simple command line based program in C. It's just a small test and the code is very simple. So what it is meant to do is to ask the user for their name, maths grade, english grade, computing grade. Then it figures out their average grade and also tells them the name they entered. Yes I know this is an extremely simple program, but I'm still doing something wrong.
The problem is, one part of my code will run first telling the user to enter their name and then once they do this and press enter the rest of my code will run all at once and then stop working. It's weird I just don't understand what is wrong.
#include <stdio.h>
int main(int argc, const char * argv[])
{
char chr;
char firstname;
int mathsmark, englishmark, computingmark, averagemark;
printf("What is your name?\n");
scanf("%c", &firstname);
printf("\n");
printf("What is your maths mark?\n");
scanf("%d", &mathsmark);
printf("\n");
printf("What is your english mark?\n");
scanf("%d", &englishmark);
printf("\n");
printf("What is your computing mark?\n");
scanf("%d", &computingmark);
printf("\n");
printf("Your name is: %c", firstname);
printf("\n");
averagemark = (mathsmark + englishmark + computingmark) / 3;
printf("%d", averagemark);
printf("\n");
chr = '\0';
while (chr != '\n') {
chr = getchar ();
}
return 0;
}
One major problem is that you've declared firstname to be a single character long, and when you try to read the name from the console, you're using the %c conversion specifier, which reads the next single character from the input stream and stores it to firstname. The remainder of the name is left in the input stream to foul up the remaining scanf calls.
For example, if you type "Jacob" as a first name, then the first scanf call assigns J to firstname, leaving "acob\n" in the input stream.
The next scanf call attempts to convert "acob\n" to an integer value and save it to mathsmark, which fails ("acob\n" is not a valid integer string). Same thing happens for the next two scanf calls.
The last loop
while (chr != '\n')
{
chr = getchar();
}
finally consumes the rest of "acob\n", which contains the newline character (because you hit Enter after typing the name), causing the loop and program to exit.
How do you fix this?
First, you need to declare firstname as an array of char:
char firstname[SOME_SIZE] = {0};
where SOME_SIZE is large enough to handle all your cases. The you need to change scanf call to
scanf("%s", firstname);
This tells scanf to read characters from the input stream up to the next whitespace character and store the results to the firstname array. Note that you don't need to use the & operator here; under most circumstances, an expression of array type will be converted ("decay") to an expression of pointer type, and the value of the expression will be the address of the first element in the array.
Note that scanf is not very safe, and it's not very robust. If you enter more characters than your buffer is sized to hold, scanf will happily store those extra characters to memory following the array, potentially clobbering something important. You can guard against this by using an explicit field width in the conversion specifier, like
scanf(*%29s", firstname);
but in general it's a pain.
scanf is also not very good at detecting bad input. If you enter "12er" as one of your marks, scanf will convert and assign the "12", leaving the "er" in the stream to foul up the next read.
scanf returns the number of successful assignments, so one way to guard against bad input is to check the return value, like so:
if (scanf("%d", &mathmarks) != 1)
{
printf("Bad input detected for math marks\n");
}
Unfortunately, scanf won't remove bad characters from the stream; you'll have to do that yourself using getchar or similar.
This is a common mistake amongst newer C/C++ developers. The scanf function detects you hitting the ENTER/RETURN key to signal the end of input, but it also catches the \n character as well at the end of the input string, so you essentially get two RETURNS being detected.
Please read up on an example of using fgets and sscanf here:
http://www.linuxforums.org/forum/programming-scripting/67560-problem-scanf.html
It will resolve this issue very quickly for you. In the meantime, I strongly urge you to check out this book:
http://www.amazon.com/Primer-Plus-5th-Stephen-Prata/dp/0672326965
It is the most commonly used C programming book in high school and colleges in North America, and has TONS of examples for you to work through, including this specific program you demonstrated above. The print version has more examples than the e-book, so I would just cough up the $30.00 for the printed version.
Good luck!
You might want to look at a few tutorials. Maybe one on Format specifiers and one on strings in C
scanf() reads data from stdin and stores them as specified by the format specifiers. In this case:
char firstname;
scanf("%c", &firstname);
Read 1 character from stdin and store it to firstname:
>> What is your first name?
Mike
Now firstname == 'M' because scanf() read 1 character as we requested.
What you wanted to do was read a string (a bunch of characters):
char firstname[5]; // an array of characters
scanf("%s", firstname); // store as a string
firstname[4] = '\0'; // Truncate the result with a NULL to insure no overflow
>> What is your first name?
Mike
Now firstname is [M][i][k][e][\0] because scanf() read 1 string, as we requested.
Note the same holds true for printf(), a printf with a %c will give you one character where as a printf() with a %s will give you all the characters until the NULL terminator.
You have (at least) two choices.
char firstname[number_big_enough_to_hold_long_name];
/*or */
char *firstname = malloc(sizeof(char) * number_big_enough_to_hold_long_name);
/* ... code ... */
free(firstname);
Further it would be best to limit width of read. scanf() does not know the size (available space) of firstname.
scanf("%number_big_enough_to_hold_long_names", ...
/* i.e. */
char firstname[32];
if(scanf("%31s", firstname) == EOF) {
perror("bad");
return 1;
}
Further you should check if there is anything left before trying next read. I.e. If someone enters "My Name" then only "My" will end up in firstname and "Name" will be left in input stream.
And getchar() returns an int not a char.
getchar
scanf
And search "ansi c char arrays tutorial" or similar.

C: combine return & scanf() in one line

I'm writing a program in C that displays a menu that asks the user to make a decision.
I want to bring up the menu multiple times in the program so I put it into function.
Each choice has a number associated with it:
(1) Add
(2) Subtract
The function displays the menu and then then I scanf the int response to a local variable and then return the variable (I of course declare the variable at the beginning of the function).
int function ()
{
int choice;
// *insert print menu code here*
scanf(" %i", &choice);
return choice;
}
Can I make this shorter by somehow doing a: return scanf(" %i", stdin);
I'm not sure if stdin would be the right choice, but that's what searching tells me.
Sure you can:
int scanInt(void) {
int result;
return (scanf(" %i", &result), result);
}
But is it useful? Probably not.
Note that this might look like undefined behaviour at first glance, because you're writing and reading from the same memory location in one expression. But it is not, as the comma operator introduces a sequence point.
You can't do this with scanf: you'd be returning the return value of scanf, which only indicates the number of items successfully read. If you need to return choice then you'll need to call scanf to fill choice and return it in two separate steps.
Another point here is that you should check the return value of scanf: it will fail if it's unable to convert the input. In your code, scanf will fail if the input is not an integer.
Furthermore, scanf reads from stdin - standard input - by default, you can't pass that as an argument unless you're using fscanf, which otherwise behaves in the same way.
Have you looked at using fgets for your input? It does return the next input line, which would allow you to do the read in the return statement since fgets returns the next line of input as a string. But you'd still need to parse the input yourself, which should probably happen within your function. In any case, fgets is typically a better choice for user input because it separates reading the input from parsing it, and avoids problems when using scanf due to input that doesn't match your format string.
You can, sort of -- but don't.
scanf returns the number of items scanned. You can't make it return the value of one of the scanned items.
return scanf(" %i", &choice), choice;
This uses the comma operator (not to be confused with the comma delimiter between function arguments), which evaluates both operands and yields the result of the right operand.
But there is no good reason to do this. There is no great virtue in making your source code more compact like this. The multi-line form is clearer, and it's easier to modify by adding error handling (what happens if the user enters something other than a number?)
Or you could write your own function that calls scanf and returns the value you want, but that still doesn't address the issue of error handling.
And if your goal is simply to put them on one line:
scanf(" %i", &choice); return choice;
But again, this is not an improvement.
No, you can't with scanf. The function needs a memory location to store the result, and doesn't return it. So you just have to live with the extra code.

why i'm having problems with scanf and gets?

when i'm trying to use scanf and gets, i'm having trouble, i need to put gets twice, if i put it once my program is terminated, it's homework and i have to use those functions in those places.
the code :
int main()
{
int i=0,operation=0;
char str[100];
printMenu();
scanf("%d",&operation);
switch (operation)
{
case 1:
printf("Please, enter your sentence >");
gets(str);
gets(str);
if (checkChars(str))
inputSent(str);
else
printf("ERROR: Incorrect data, try again.");
break;
}
return 0;
}
So, we have a stdin stream of data. This is the input that you write at the terminal.
When you call scanf, it only reads the decimal number that you wrote. It does not read the newline after it. Therefore, you need two calls to gets because the first call only sees '\n' while the second sees your actual data.
To fix this, I'd recommend that you use gets to read the full line when you get the operation, and then use sscanf to get the operation.
For example:
printMenu();
gets(str)
sscanf(str, "%d", &operation);
switch (operation) {
What's happening is after your first scanf() is that there's still data lingering in the input buffer (any form of whitespace that delimits things, usually a return) and when you call the next gets() it returns immediately because it read the "enter" you hit after typing in a number.
In order to solve this, you'll need to flush the input buffer. This is good practice especially when switching between reading individual character words and sentences. Here's a small snippet I found from another thread that may help you (this would go after your scanf()):
while (getchar() != EOF);
What it does is continually read characters out of the input buffer until none are left. This may not be the best solutions for all situations but it should be enough to help you through this assignment.

Parsing input with scanf in C

I've been having a lot of problems trying to figure out how to use scanf(). It seems to work fine with integers, being fairly straight forward scanf("%d", &i).
Where I am running into issues is using scanf() in loops trying to read input. For example:
do {
printf("counter: %d: ", counter);
scanf("%c %c%d", &command, &prefix, &input);
} while (command != 'q');
When I enter in a validly structured input like c P101, it seems to loop again before prompting me. This seems to happen even with a single:
scanf("%c", &c)
in a while loop. It'll do the loop twice before prompting me again. What is making it loop twice, and how do I stop it?
When I enter in less amount of input that programmatically wouldn't have another character or number such as q, pressing enter seems to prompt me to enter more. How do I get scanf() to process both single and double character entries?
When you enter "c P101" the program actually receives "c P101\n". Most of the conversion specifiers skip leading whitespace including newlines but %c does not. The first time around everything up til the "\n" is read, the second time around the "\n" is read into command, "c" is read into prefix, and "P" is left which is not a number so the conversion fails and "P101\n" is left on the stream. The next time "P" is stored into command, "1" is stored into prefix, and 1 (from the remaining "01") is stored into input with the "\n" still on the stream for next time. You can fix this issue by putting a space at the beginning of the format string which will skip any leading whitespace including newlines.
A similiar thing is happening for the second case, when you enter "q", "q\n" is entered into the stream, the first time around the "q" is read, the second time the "\n" is read, only on the third call is the second "q" read, you can avoid the problem again by adding a space character at the beginning of the format string.
A better way to do this would be to use something like fgets() to process a line at a time and then use sscanf() to do the parsing.
It's really broken! I didn't know it
#include <stdio.h>
int main(void)
{
int counter = 1;
char command, prefix;
int input;
do
{
printf("counter: %d: ", counter);
scanf("%c %c%d", &command, &prefix, &input);
printf("---%c %c%d---\n", command, prefix, input);
counter++;
} while (command != 'q');
}
counter: 1: a b1
---a b1---
counter: 2: c d2
---
c1---
counter: 3: e f3
---d 21---
counter: 4: ---e f3---
counter: 5: g h4
---
g3---
The output seems to fit with Robert's answer.
Once you have the string that contains the line. i.e. "C P101", you can use the parsing abilities of sscanf.
See:
http://www.cplusplus.com/reference/clibrary/cstdio/sscanf.html
For question 1, I suspect that you've got a problem with your printf(), since there is no terminating "\n".
The default behavior of printf is to buffer output until it has a complete line. That is unless you explicitly change the buffering on stdout.
For question 2, you've just hit one of the biggest problems with scanf(). Unless your input exactly matches the scan string that you've specified, your results are going to be nothing like what you expect.
If you've got an option you'll have better results (and fewer security issues) by ignoring scanf() and doing your own parsing. For example, use fgets() to read an entire line into a string, and then process the individual fields of the string — maybe even using sscanf().
Perhaps using a while loop, not a do...while loop will help. This way the condition is tested before execution of the code.
Try the following code snippet:
while(command != 'q')
{
//statements
}
Also, if you know the string length ahead of time, 'for' loops can be much easier to work with than 'while' loops. There are also some trickier ways to dynamically determine the length as well.
As a final rant: scanf() does not "suck." It does what it does and that is all.
The gets() function is very dangerous (though convenient for no-risk applications), since it does not natively do any checking of the input. It is VERY commonly known as a point of exploit, specifically buffer overflow attacks, overwriting space in registers not allocated for that variable. Therefore if you choose to use it, spend some time putting some solid error checking/correction in.
However, almost invariably, either fgets() or POSIX getline() should be used to read the line — noting that the functions both include the newline in the input string, unlike gets(). You can remove the trailing newline from string read by either fgets() or getline() using string[strcspn(string, "\n")] = '\0'; — this works reliably.

Resources