formatted string input and error in C - c

struct account {
float interestRate;
char accountType[21];
};
void writeAccounts() {
struct account Acc;
FILE *fp = fopen("test.txt", "a");
printf("New interestRate : ");
scanf("%f", &Acc.interestRate);
printf("New accountType : ");
scanf("%*c%20[^\n]", Acc.accountType);
fprintf(fp, "%.2f %s\n", Acc.interestRate, Acc.accountType);
fclose(fp);
}
int main() {
int select = 0;
do {
scanf("%d", &select);
switch (select) {
case 1:
displayAccounts();
break;
case 2:
getRecNo();
break;
case 3:
writeAccounts();
break;
}
} while (select != 0);
return 0
Above is my code(deleted some unrelated functions) in C.
When I try to type a string more than 20 characters for the accountType in "writeAccounts" function, program starts to skip few steps until it saves all the characters I have input.
(CANNOT USE FGETS!! my prof doesnt allow to :/)
http://imgur.com/a/eKQRu
why is this happening?
isn't %20[^\n] means its only going to accept 20 characters even if user puts more?
possible way to prevent this ?

why is this happening?
The excess input remains in the input buffer and is read by subsequent scanf calls.
isn't %20[^\n] means its only going to accept 20 characters even if user puts more?
That is correct, and as mentioned any remaining input is not processed.
possible way to prevent this ?
After a successful scan (20 characters or less) the newline remains in the input buffer. You can do various things.
Firstly you could check if the next input character is the expected newline:
if (getchar() != '\n') {
// add code here to report the error and take evasive action
}
Or you could simply truncate and ignore any excess input by cleaning off the buffer:
int c;
while ((c = getchar()) != '\n' && c != EOF) {
// eats up the input buffer, the code is in the loop control
}

Related

Issues with scanf() and accepting user input

I am trying to take in user input with spaces and store it in an array of characters.
After, I want to take in a single character value and store it as a char.
However, when I run my code, the prompt for the character gets ignored and a space is populated instead. How can I take in an array of chars and still be allowed to prompt for a single character after?
void main()
{
char userIn[30];
char findChar;
printf("Please enter a string: ");
scanf("%[^\n]s", userIn);
printf("Please enter a character to search for: ");
scanf("%c", &findChar);
//this was put here to see why my single char wasnt working in a function I had
printf("%c", findChar);
}
scanf("%c", &findChar); reads the next character pending in the input stream. This character will be the newline entered by the user that stopped the previous conversion, so findChar will be set to the value '\n', without waiting for any user input and printf will output this newline without any other visible effect.
Modify the call as scanf(" %c", &findChar) to ignore pending white space and get the next character from the user, or more reliably write a loop to read the read and ignore of the input line.
Note also that scanf("%[^\n]s", userIn); is incorrect:
scanf() may store bytes beyond the end of userIn if the user types more than 29 bytes of input.
the s after the ] is a bug, the conversion format for character classes is not a variation of the %s conversion.
Other problems:
void is not a proper type for the return value of the main() function.
the <stdio.h> header is required for this code.
Here is a modified version:
#include <stdio.h>
int main() {
char userIn[30];
int c;
char findChar;
int i, found;
printf("Please enter a string: ");
if (scanf("%29[^\n]", userIn) != 1) {
fprintf(stderr, "Input failure\n");
return 1;
}
/* read and ignore the rest of input line */
while ((c = getchar()) != EOF && c != '\n')
continue;
printf("Please enter a character to search for: ");
if (scanf("%c", &findChar) != 1) {
fprintf(stderr, "Input failure\n");
return 1;
}
printf("Searching for '%c'\n", findChar);
found = 0;
for (i = 0; userIn[i] != '\0'; i++) {
if (userIn[i] == findChar) {
found++;
printf("found '%c' at offset %d\n", c, i);
}
}
if (!found) {
printf("character '%c' not found\n", c);
}
return 0;
}
scanf("%[^\n]s", userIn); is a bit weird. The s is guaranteed not to match, since that character will always be \n. Also, you should use a width modifier to avoid a buffer overflow. Use scanf("%29[^\n]", userIn); That alone will not solve the problem, since the next scanf is going to consume the newline. There are a few options. You could consume the newline in the first scanf with:
scanf("%29[^\n]%*c", userIn);
or discard all whitespace in the next call with
scanf(" %c", &findChar);
The behavior will differ on lines of input that exceed 29 characters in length or when the user attempts to assign whitespace to findChar, so which solution you use will depend on how you want to handle those situations.

Looping Input Validation for a double type, but the user inputs "abc" as input

If the user inputs "abc" as the input, the Do-While Loop loops around 3 times, one time for each letter. However, I want it to loop only once.
int main(void) {
do {
printf("Enter how much money you can contribute: ");
numArgsRead = scanf(" %lf", &availableFundsToContribute);
scanf("%c", &finalValOnLine);
} while (!((availableFundsToContribute > 0) && (numArgsRead == 1) && (finalValOnLine == '\n')));
return 0;
}
Here's what my output looks like:
Enter how much money you can contribute: abc
Enter how much money you can contribute: Enter how much money you can contribute: Enter how much money you can contribute:
Here's what I want it to look like:
Enter how much money you can contribute: abc
Enter how much money you can contribute:
There are two main options — continuing with scanf() and switching to fgets() and sscanf().
Continuing with scanf()
As noted in comments, you need to track when scanf() fails to convert the input without encountering EOF, and deal with the extraneous input. The normal way to deal with it is to read up to and including the end of line.
static inline int gobble_to_eol(void)
{
int c;
while ((c = getchar()) != EOF && c != '\n')
;
return c;
}
int main(void)
{
int numArgsRead;
double availableFundsToContribute = 0.0;
char finalValueOnLine = '\0';
do
{
printf("Enter how much money you can contribute: ");
if ((numArgsRead = scanf("%lf", &availableFundsToContribute)) == EOF)
break;
if (numArgsRead == 0)
{
if (gobble_to_eol() == EOF)
break;
}
else
{
if (scanf("%c", &finalValOnLine) == EOF)
break;
if (c != '\n')
{
/* Input might be 3.14abc */
if (gobble_to_eol() == EOF)
break;
}
} while (!(numArgsRead == 1 && availableFundsToContribute > 0.0 && finalValOnLine == '\n'));
return 0;
}
Point of detail: all formats except three (%c, %[…] (scan sets), and %n) skip leading white space, including newlines, automatically. There is no need include the blank in "%lf" (but there is also no harm in doing so at the start, but putting a blank at the end is a UI disaster).
Switching to fgets() and sscanf()
An alternative approach reads whole lines and then scans them with sscanf(). This was mentioned by user3386109 in a comment, but it is standard advice and often the best way to deal with the input.
The code might look like this:
int main(void)
{
char buffer[4096]; // Make it bigger if you prefer!
double availableFundsToContribute = 0.0;
char finalValOnLine;
do
{
printf("Enter how much money you can contribute: ");
if (fgets(buffer, sizeof(buffer), stdin) == NULL)
break;
if ((numArgsRead = sscanf(buffer, "%lf%c", &availableFundsToContribute, &finalValOnLine)) != 2)
{
buffer[strcspn(buffer, "\n")] = '\0'; // Zap newline
fprintf(stderr, "Unrecognized input [%s] - try again.\n", buffer);
}
} while (!(numArgsRead == 2 && availableFundsToContribute > 0 && finalValOnLine == '\n'));
return 0;
}
In both cases, I would probably use a top-controlled loop (for or while) but I'm keeping close to the original code. There probably should be error reporting associated with the call to gobble_to_eol() in the first code, so that the user knows what the program thinks is wrong. Note that using scanf() directly makes it hard to report on what caused the problem; reading a line and then parsing it means you can report the erroneous input accurately. Note, too, that using fgets() means that empty lines will be reported as an error, rather than silently ignored waiting for new input.
Warning: no compiler was consulted about the accuracy of the code. Treat with due caution.

Program is not accepting second string and giving output directly

Here I want to compare two strings in case2. case 1 works well but when I go to case 2 it's not asking for second string input and directly prints "Both strings are different"[1]
[1]: https://i.stack.imgur.com/l2J6L.jpg
#include <stdio.h>
#include <stdlib.h>
#define size 20
int main ()
{
char str1[size],str2[size];
int operation,error=0,i=0;
printf("Enter String: ");
fgets(str1, size, stdin);
do {
printf("1.Copy\n2.Compare\n3.Exit\nWhich operation you want to do:");
scanf("%d",&operation);
switch (operation) {
case 1:
for (int i=0; str1[i] != '\0'; i++) {
str2[i] = str1[i];
}
printf("First string: %s\n",str1);
printf("Second string: %s\n",str2);
break;
default:
printf("Error");
break;
case 2:
printf("Enter second string: "); // it's not executing (Not takin input) and directly i get o/p of line 39
fgets(str2, size, stdin);
for (i=0; str2[i] != '\0'; i++) {
if (str1[i] != str2[i]) {
error++;
}
}
if (error == 0) {
printf("Both strings are same.\n");
}
else
printf("Both strings are not same.\n");
break;
}
} while (operation != 3);
}
As scanf leaves behind a dangling newline character \n it causes the fgets to not wait for the input from the user. Try flushing the input buffer by using getchar.
Update: Added loop to remove all the characters which are skipped by the scanf but can be entered by the user like extra white spaces after the number.
...
do {
printf("1.Copy\n2.Compare\n3.Exit\nWhich operation you want to do:");
scanf("%d",&operation);
int ch;
while ((ch = getchar()) != '\n' && ch != EOF);
switch (operation) {
...
Reference: faq.cprogramming.com
Since your strings are lines, scanf() is not a good choice except for getting the integer value, and to clean the buffer of everything after that (might be all sorts of junk, never trust a user), do a fgets into str2. Your new lines will compare, too, if they are identical. You should also test the return from scanf is 1, saying you got a number! What if a user types in 'x' as the number? If you want to ask again, you need to clean the junk out of the buffer. Remember that, since you have 'cooked' input, nothing is sent until the user hits enter, so you always need to deal with the new line character. If all you do is scanf in numbers, scanf will got through an new line as white space seeking a digit, but you are doing mixed string and number input.
You need to compare a null to mis-compare if one string is a prefix of the other, so in 'for' test 'i < size' but break out of the loop if both strings have a null at the same point ( !str1[i] && !str2[i] ) or on the first miscompare (setting error). There is no point in comparing past the first miss! In the prefix case, the null mis-compares some other char value.
Since trailing spaces are part of your string, you might print them in single quotes (after removing the new line).

want to call fgets() multiple times based on users need

User enters '1' or '0' choice to continue getting string using fgets(). So when the user enters the choice, fgets reads it from console. I am storing it in another variable. But fgets gets the choice and stores it in messages. I have tried using fflush(stdin) after receiving the choice. Please help me out.
int main() {
int choice=1;
char *message;
int i=0;
while (choice == 1) {
fflush(stdout);
printf("Enter the message: ");
fflush(stdout);
message = fgets(message,200,stdin);
while (message[i]!='\n') {
i++;
}
message[i] = '\0';
send_message(message);
printf("\nType '1' to continue or '0' to quit: ");
scanf("%d",&choice);
fflush(stdin);
}
}
It looks like you're trying to scanf() to read the user's input -- this is inherently dangerous. (See https://www.reddit.com/r/learnprogramming/comments/1d0w4x/c_scanf_d_but_error_if_user_enters_a_character/).
I'd recommend either using %s for your format string, or better yet, build a subroutine to do safe input and parse it the old-fashioned way, such as something along these lines:
/* getsafe() - Generic input using the preferred input method rather than gets() */
#include <stdio.h>
#include <string.h>
char *getsafe(char *inpstr,int inpsiz) {
char *seachr; /* Result of search via strchr() */
if (inpstr==NULL) {
return(NULL);
}
if (fgets(inpstr,inpsiz,stdin)==NULL) {
return(NULL);
}
seachr=strchr(inpstr,'\n');
if (seachr!=NULL) *seachr=0;
return(inpstr);
}
That way you can specify the buffer length and provide a string (array of characters) of sufficient length as to prevent buffer overruns (security issue), and then parse the [0] position in that array for your answer.
#define ANSSIZ 80 /* Maximum allowed size of user answer */
char usrans[ANSSIZ]; /* User Answer */
printf("Enter 'y' or 'n': ");
getsafe(usrans, ANSSIZ-1);
There's a lot of problems with this - It probably belongs on Code Review
However, here is a critique on some of the major problems
int main() {
int choice=1;
char *message; // This is a pointer, but is not malloc'ed. You might want "char message[200]" instead?
int i=0; // This is the only time "i" is set to 0. It needs to be reset at the start of the loop
while (choice == 1) {
fflush(stdout); // No need for this
printf("Enter the message: ");
fflush(stdout);
message = fgets(message,200,stdin);
while (message[i]!='\n') { // Why not use strlen?
i++; // "i" can keep growing forever if there is no newline (if someone entered 199 characters before pressing enter)
}
message[i] = '\0'; // fgets does this for you - The past loop was pointless
send_message(message);
printf("\nType 'y' to continue or 'n' to quit: "); // You forgot to flush here!
scanf("%d",&choice); // I don't think this will result in a 0 or 1 output... %d is for a digit, and you're asking the user for y or n.
fflush(stdin); // This is invalid and unneeded - You can't flush stdin
}
}

scanf validation sits and waits for another input. Why?

I was working on this sample exercise, and everything works as I would like it to, but there is one behavior I don't understand.
When providing input: if I make consecutive invalid entries everything seems to work great. But if I enter a number different from 1,2,3 in the case of the first question, or 1,2 in the case of the second question, the program just sits there until a new input is given. If another invalid entry is made, it goes back to the error "invalid entry" message, and if an appropriate number is entered, everything moves along fine.
I do not understand why it stops to wait for a second input...anyone?
Thanks guys.
#include <stdio.h>
static int getInt(const char *prompt)
{
int value;
printf("%s",prompt);
while (scanf("%d", &value) !=1)
{
printf("Your entry is invalid.\nGive it another try: %s", prompt);
getchar();
scanf("%d", &value);
}
return value;
}
int main() {
int wood_type, table_size, table_price;
printf("Please enter " );
wood_type = getInt("1 for Pine, 2 for Oak, and 3 for Mahogany: ");
printf("Please enter ");
table_size = getInt("1 for large, 2 for small: ");
printf("\n");
switch (wood_type) {
case 1:
table_price = (table_size == 1)? 135:100;
printf("The cost of for your new table is: $%i", table_price);
break;
case 2:
table_price = (table_size == 1)? 260:225;
printf("The cost of for your new table is: $%i", table_price);
break;
case 3:
table_price = (table_size == 1)? 345:310;
printf("The cost of for your new table is: $%i", table_price);
break;
default:
table_price = 0;
printf("The cost of for your new table is: $%i", table_price);
break;
}
}
You most likely need to flush your input buffer (especially with multiple scanf calls in a function). After scanf, a newline '\n' remains in the input buffer. fflush does NOT do this, so you need to do it manually. A simple do...while loop works. Give it a try:
edit:
static int getInt(const char *prompt)
{
int value;
int c;
while (printf (prompt) && scanf("%d", &value) != 1)
{
do { c = getchar(); } while ( c != '\n' && c != EOF ); // flush input
printf ("Invalid Entry, Try Again...");
}
return value;
}
The blank line you get if you enter nothing is the normal behavior of scanf. It is waiting for input (some input). If you want your routine to immediately prompt again in the case the [Enter] key is pressed, then you need to use another routine to read stdin like (getline or fgets). getline is preferred as it returns the number of characters read (which you can test). You can then use atoi (in <stdlib.h>) to convert the string value to an integer. This will give you the flexibility you need.
example:
int newgetInt (char *prompt)
{
char *line = NULL; /* pointer to use with getline () */
ssize_t read = 0; /* number of characters read */
size_t n = 0; /* numer of chars to read, 0 no limit */
static int num = 0; /* number result */
while (printf ("\n %s ", prompt) && (read = getline (&line, &n, stdin)) != -1)
{
if ((num = atoi (line)))
break;
else
printf ("Invalid Input, Try Again...\n");
}
return num;
}
If some invalid input is entered, it stays in the input buffer.
The invalid input must be extracted before the scanf function is completed.
A better method is to get the whole line of input then work on that line.
First, put that input line into a temporary array using fgets(),
then use sscanf() (safer than scanf because it guards against overflow).
#include <stdio.h>
int main(int argc, const char * argv[]) {
char tempbuff[50];
int result, d , value;
do
{
printf("Give me a number: ");
fgets( tempbuff, sizeof(tempbuff), stdin ); //gets string, puts it into tempbuff via stdin
result = sscanf(tempbuff, "%d", &value); //result of taking buffer scanning it into value
if (result < 1){ //scanf can return 0, # of matched conversions,
//(1 in this case), or EOF.
printf("You didn't type a number!\n");
}
}while (result < 1);
//some code
return 0;
}
Knowledge from: http://www.giannistsakiris.com/2008/02/07/scanf-and-why-you-should-avoid-using-it/

Resources