I am trying to work on a employee management system that takes employee information as input and prints the output accordingly.
Structure:
struct employee
{
char empId[10];
char name[20];
char empType[10];
int dd, mm, yyyy;
};
Program to check if the string contains only integers:
void checkValidId(char *id)
{
int count = 0, i;
for(i = 0; i < strlen(id); i++)
{
if(id[i] >= '0' && id[i] <= '9')
{
count++;
}
}
if(count == strlen(id))
// continue executing next statements of input() function
else
// take the empId input again
}
Function to take input:
struct employee input()
{
struct employee e;
printf("Enter Employee ID: ");
scanf("%s", &e.empId);
checkValidId(e.empId);
// next statements ....
}
Here I am trying to check if the string input that is empId contains only integers which is done by checkValidId(). If yes then the program continues executing next statements. Else I want to take the input empId again. How to do it.
Requesting help!
Thanks!
Include the header <ctype.h>, and use isdigit to test if a character is in the set '0' ... '9'.
Include the header <stdbool.h>, and change the signature of checkValidId to
bool checkValidId(const char *id)
in order to indicate a result to the calling function.
In checkValidId, loop through each character of the string. If the current character is not a digit, immediately return false from the function. If the loop finishes, all the characters must be digits, so you can then return true.
Note that there is no reason to call strlen here. Simply loop until the current character is the null-terminating byte.
&e.empId is of type char (*)[10], that is a pointer-to-array-of-10-char. The scanf specifier "%s" expects the type char *, or pointer-to-char. An array will decay to a pointer to its first element when passed to a function, so the the 'correct' way to call scanf here is scanf("%s", e.empId);.
That said, you must check that the return value of scanf is the expected number of conversions, otherwise you will be operating on indeterminate data.
Additionally, an unbound "%s" is as dangerous as the gets function, as it does not know when to stop reading data, and will easily overflow the provided buffer. You must provide a maximum field width to prevent scanf from reading too many characters. This should be at most the size of your buffer minus 1, leaving room for the null-terminating byte.
An example of using scanf safely:
if (1 != scanf("%9s", e.empId)) {
/* handle input stream error */
}
Note that when scanf fails to perform a conversion, the problem data is left in the stream. Recovering from bad input with scanf is very hard, and for that reason a line-based approach to user input is generally advised. This can be accomplished with fgets.
If there is room in the buffer, fgets includes the newline character when read. See Removing trailing newline character from fgets() input for an example usage of strcspn, which can also be used as a way to get the length of the input.
To repeatedly ask the user for input, use an infinite loop. Only break from the loop when the user correctly enters the requested data, or a critical error occurs.
Here is an example program, using the methods discussed:
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
struct employee {
char empId[10];
/* ... */
};
bool checkValidId(const char *id)
{
for (size_t i = 0; id[i] != '\0'; i++)
if (!isdigit((unsigned char) id[i]))
return false;
return true;
}
bool getEmployee(struct employee *e, FILE *stream)
{
char buffer[256];
while (1) {
printf("Enter employee ID: ");
if (!fgets(buffer, sizeof buffer, stream))
return false;
size_t length = strcspn(buffer, "\r\n");
/* remove the newline */
buffer[length] = '\0';
if (!length)
fprintf(stderr, "Invalid ID. Zero length.\n");
else if (length >= sizeof e->empId)
fprintf(stderr, "Invalid ID. Too long.\n");
else if (!checkValidId(buffer))
fprintf(stderr, "Invalid ID. Contains non-digit character(s).\n");
else {
strcpy(e->empId, buffer);
break;
}
puts("Try again.");
}
return true;
}
int main(void)
{
struct employee emp;
if (!getEmployee(&emp, stdin))
return 1;
printf("ID: <%s>\n", emp.empId);
}
Interacting with this program:
Enter employee ID: foobar
Invalid ID. Contains non-digit character(s).
Try again.
Enter employee ID:
Invalid ID. Zero length.
Try again.
Enter employee ID: 1234567890
Invalid ID. Too long.
Try again.
Enter employee ID: 42
ID: <42>
checkValidId should return int so it can tell the caller whether the id is valid [or not].
A caller (e.g. input) should loop if the return value of checkValidId indicates a bad id.
No need to use strlen at all.
No need to compare lengths. A simpler algorithm can be used.
We can just stop the loop if an invalid char is detected
Here is the refactored code:
struct employee {
char empId[10];
char name[20];
char empType[10];
int dd, mm, yyyy;
};
// Program to check if the string contains only integers:
// RETURNS: 1=input valid, 0=retry
int
checkValidId(char *id)
{
int valid = 0;
for (int i = 0; id[i] != 0; i++) {
valid = (id[i] >= '0') && (id[i] <= '9');
if (! valid)
break;
}
return valid;
}
// Function to take input:
struct employee
input(void)
{
struct employee e;
while (1) {
printf("Enter Employee ID: ");
scanf("%s", &e.empId);
if (checkValidId(e.empId))
break;
}
// next statements ....
}
Related
Using the following code:
char *name = malloc(sizeof(char) + 256);
printf("What is your name? ");
scanf("%s", name);
printf("Hello %s. Nice to meet you.\n", name);
A user can enter their name but when they enter a name with a space like Lucas Aardvark, scanf() just cuts off everything after Lucas. How do I make scanf() allow spaces
People (and especially beginners) should never use scanf("%s") or gets() or any other functions that do not have buffer overflow protection, unless you know for certain that the input will always be of a specific format (and perhaps not even then).
Remember than scanf stands for "scan formatted" and there's precious little less formatted than user-entered data. It's ideal if you have total control of the input data format but generally unsuitable for user input.
Use fgets() (which has buffer overflow protection) to get your input into a string and sscanf() to evaluate it. Since you just want what the user entered without parsing, you don't really need sscanf() in this case anyway:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Maximum name size + 1. */
#define MAX_NAME_SZ 256
int main(int argC, char *argV[]) {
/* Allocate memory and check if okay. */
char *name = malloc(MAX_NAME_SZ);
if (name == NULL) {
printf("No memory\n");
return 1;
}
/* Ask user for name. */
printf("What is your name? ");
/* Get the name, with size limit. */
fgets(name, MAX_NAME_SZ, stdin);
/* Remove trailing newline, if there. */
if ((strlen(name) > 0) && (name[strlen (name) - 1] == '\n'))
name[strlen (name) - 1] = '\0';
/* Say hello. */
printf("Hello %s. Nice to meet you.\n", name);
/* Free memory and exit. */
free (name);
return 0;
}
Try
char str[11];
scanf("%10[0-9a-zA-Z ]", str);
This example uses an inverted scanset, so scanf keeps taking in values until it encounters a '\n'-- newline, so spaces get saved as well
#include <stdio.h>
int main (int argc, char const *argv[])
{
char name[20];
// get up to buffer size - 1 characters (to account for NULL terminator)
scanf("%19[^\n]", name);
printf("%s\n", name);
return 0;
}
You can use this
char name[20];
scanf("%20[^\n]", name);
Or this
void getText(char *message, char *variable, int size){
printf("\n %s: ", message);
fgets(variable, sizeof(char) * size, stdin);
sscanf(variable, "%[^\n]", variable);
}
char name[20];
getText("Your name", name, 20);
DEMO
Don't use scanf() to read strings without specifying a field width. You should also check the return values for errors:
#include <stdio.h>
#define NAME_MAX 80
#define NAME_MAX_S "80"
int main(void)
{
static char name[NAME_MAX + 1]; // + 1 because of null
if(scanf("%" NAME_MAX_S "[^\n]", name) != 1)
{
fputs("io error or premature end of line\n", stderr);
return 1;
}
printf("Hello %s. Nice to meet you.\n", name);
}
Alternatively, use fgets():
#include <stdio.h>
#define NAME_MAX 80
int main(void)
{
static char name[NAME_MAX + 2]; // + 2 because of newline and null
if(!fgets(name, sizeof(name), stdin))
{
fputs("io error\n", stderr);
return 1;
}
// don't print newline
printf("Hello %.*s. Nice to meet you.\n", strlen(name) - 1, name);
}
getline()
Now part of POSIX, none-the-less.
It also takes care of the buffer allocation problem that you asked about earlier, though you have to take care of freeing the memory.
You can use the fgets() function to read a string or use scanf("%[^\n]s",name); so string reading will terminate upon encountering a newline character.
If someone is still looking, here's what worked for me - to read an arbitrary length of string including spaces.
Thanks to many posters on the web for sharing this simple & elegant solution.
If it works the credit goes to them but any errors are mine.
char *name;
scanf ("%m[^\n]s",&name);
printf ("%s\n",name);
You may use scanf for this purpose with a little trick. Actually, you should allow user input until user hits Enter (\n). This will consider every character, including space. Here is example:
int main()
{
char string[100], c;
int i;
printf("Enter the string: ");
scanf("%s", string);
i = strlen(string); // length of user input till first space
do
{
scanf("%c", &c);
string[i++] = c; // reading characters after first space (including it)
} while (c != '\n'); // until user hits Enter
string[i - 1] = 0; // string terminating
return 0;
}
How this works? When user inputs characters from standard input, they will be stored in string variable until first blank space. After that, rest of entry will remain in input stream, and wait for next scanf. Next, we have a for loop that takes char by char from input stream (till \n) and apends them to end of string variable, thus forming a complete string same as user input from keyboard.
Hope this will help someone!
While you really shouldn't use scanf() for this sort of thing, because there are much better calls such as gets() or getline(), it can be done:
#include <stdio.h>
char* scan_line(char* buffer, int buffer_size);
char* scan_line(char* buffer, int buffer_size) {
char* p = buffer;
int count = 0;
do {
char c;
scanf("%c", &c); // scan a single character
// break on end of line, string terminating NUL, or end of file
if (c == '\r' || c == '\n' || c == 0 || c == EOF) {
*p = 0;
break;
}
*p++ = c; // add the valid character into the buffer
} while (count < buffer_size - 1); // don't overrun the buffer
// ensure the string is null terminated
buffer[buffer_size - 1] = 0;
return buffer;
}
#define MAX_SCAN_LENGTH 1024
int main()
{
char s[MAX_SCAN_LENGTH];
printf("Enter a string: ");
scan_line(s, MAX_SCAN_LENGTH);
printf("got: \"%s\"\n\n", s);
return 0;
}
/*reading string which contains spaces*/
#include<stdio.h>
int main()
{
char *c,*p;
scanf("%[^\n]s",c);
p=c; /*since after reading then pointer points to another
location iam using a second pointer to store the base
address*/
printf("%s",p);
return 0;
}
Using the following code:
char *name = malloc(sizeof(char) + 256);
printf("What is your name? ");
scanf("%s", name);
printf("Hello %s. Nice to meet you.\n", name);
A user can enter their name but when they enter a name with a space like Lucas Aardvark, scanf() just cuts off everything after Lucas. How do I make scanf() allow spaces
People (and especially beginners) should never use scanf("%s") or gets() or any other functions that do not have buffer overflow protection, unless you know for certain that the input will always be of a specific format (and perhaps not even then).
Remember than scanf stands for "scan formatted" and there's precious little less formatted than user-entered data. It's ideal if you have total control of the input data format but generally unsuitable for user input.
Use fgets() (which has buffer overflow protection) to get your input into a string and sscanf() to evaluate it. Since you just want what the user entered without parsing, you don't really need sscanf() in this case anyway:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Maximum name size + 1. */
#define MAX_NAME_SZ 256
int main(int argC, char *argV[]) {
/* Allocate memory and check if okay. */
char *name = malloc(MAX_NAME_SZ);
if (name == NULL) {
printf("No memory\n");
return 1;
}
/* Ask user for name. */
printf("What is your name? ");
/* Get the name, with size limit. */
fgets(name, MAX_NAME_SZ, stdin);
/* Remove trailing newline, if there. */
if ((strlen(name) > 0) && (name[strlen (name) - 1] == '\n'))
name[strlen (name) - 1] = '\0';
/* Say hello. */
printf("Hello %s. Nice to meet you.\n", name);
/* Free memory and exit. */
free (name);
return 0;
}
Try
char str[11];
scanf("%10[0-9a-zA-Z ]", str);
This example uses an inverted scanset, so scanf keeps taking in values until it encounters a '\n'-- newline, so spaces get saved as well
#include <stdio.h>
int main (int argc, char const *argv[])
{
char name[20];
// get up to buffer size - 1 characters (to account for NULL terminator)
scanf("%19[^\n]", name);
printf("%s\n", name);
return 0;
}
You can use this
char name[20];
scanf("%20[^\n]", name);
Or this
void getText(char *message, char *variable, int size){
printf("\n %s: ", message);
fgets(variable, sizeof(char) * size, stdin);
sscanf(variable, "%[^\n]", variable);
}
char name[20];
getText("Your name", name, 20);
DEMO
Don't use scanf() to read strings without specifying a field width. You should also check the return values for errors:
#include <stdio.h>
#define NAME_MAX 80
#define NAME_MAX_S "80"
int main(void)
{
static char name[NAME_MAX + 1]; // + 1 because of null
if(scanf("%" NAME_MAX_S "[^\n]", name) != 1)
{
fputs("io error or premature end of line\n", stderr);
return 1;
}
printf("Hello %s. Nice to meet you.\n", name);
}
Alternatively, use fgets():
#include <stdio.h>
#define NAME_MAX 80
int main(void)
{
static char name[NAME_MAX + 2]; // + 2 because of newline and null
if(!fgets(name, sizeof(name), stdin))
{
fputs("io error\n", stderr);
return 1;
}
// don't print newline
printf("Hello %.*s. Nice to meet you.\n", strlen(name) - 1, name);
}
getline()
Now part of POSIX, none-the-less.
It also takes care of the buffer allocation problem that you asked about earlier, though you have to take care of freeing the memory.
You can use the fgets() function to read a string or use scanf("%[^\n]s",name); so string reading will terminate upon encountering a newline character.
If someone is still looking, here's what worked for me - to read an arbitrary length of string including spaces.
Thanks to many posters on the web for sharing this simple & elegant solution.
If it works the credit goes to them but any errors are mine.
char *name;
scanf ("%m[^\n]s",&name);
printf ("%s\n",name);
You may use scanf for this purpose with a little trick. Actually, you should allow user input until user hits Enter (\n). This will consider every character, including space. Here is example:
int main()
{
char string[100], c;
int i;
printf("Enter the string: ");
scanf("%s", string);
i = strlen(string); // length of user input till first space
do
{
scanf("%c", &c);
string[i++] = c; // reading characters after first space (including it)
} while (c != '\n'); // until user hits Enter
string[i - 1] = 0; // string terminating
return 0;
}
How this works? When user inputs characters from standard input, they will be stored in string variable until first blank space. After that, rest of entry will remain in input stream, and wait for next scanf. Next, we have a for loop that takes char by char from input stream (till \n) and apends them to end of string variable, thus forming a complete string same as user input from keyboard.
Hope this will help someone!
While you really shouldn't use scanf() for this sort of thing, because there are much better calls such as gets() or getline(), it can be done:
#include <stdio.h>
char* scan_line(char* buffer, int buffer_size);
char* scan_line(char* buffer, int buffer_size) {
char* p = buffer;
int count = 0;
do {
char c;
scanf("%c", &c); // scan a single character
// break on end of line, string terminating NUL, or end of file
if (c == '\r' || c == '\n' || c == 0 || c == EOF) {
*p = 0;
break;
}
*p++ = c; // add the valid character into the buffer
} while (count < buffer_size - 1); // don't overrun the buffer
// ensure the string is null terminated
buffer[buffer_size - 1] = 0;
return buffer;
}
#define MAX_SCAN_LENGTH 1024
int main()
{
char s[MAX_SCAN_LENGTH];
printf("Enter a string: ");
scan_line(s, MAX_SCAN_LENGTH);
printf("got: \"%s\"\n\n", s);
return 0;
}
/*reading string which contains spaces*/
#include<stdio.h>
int main()
{
char *c,*p;
scanf("%[^\n]s",c);
p=c; /*since after reading then pointer points to another
location iam using a second pointer to store the base
address*/
printf("%s",p);
return 0;
}
I am making a typedef to a structure for a "person" The person has a name, ssn and yearOfBirth. I am getting errors I do not understand with my for loops.
[Error] cannot convert 'person_t' to 'person_t*' for argument '1' to
'void getOnePerson(person_t*)'
This is the first file:
#include <stdio.h>
#include <string.h>
#include "structures.h"
void getOnePerson(person_t *p)
{
printf("Enter full name: ");
scanf("%99[^\n]", p -> name);
printf("Enter ssn: ");
scanf("%99[^\n]", p -> ssn);
printf("Enter year of birth: ");
scanf("%d", &p -> yearOfBirth);
}
void printOnePerson(person_t p)
{
printf("%s:", p.name);
printf("%s:", p.ssn);
printf("%s\n", p.yearOfBirth);
}
void getPeople(person_t p[], int numOfPeople)
{
for(int i = 0; i < sizeof(p); i++)
{
getOnePerson(p[i]);
}
}
void printPeople(person_t p[], int numOfPeople)
{
for(int i = 0; i < sizeof(p); i++)
{
printOnePerson(p[i]);
}
}
This is my structure file:
#define NAME_SIZE 80
#define SSN_SIZE 13
#define NUM_PEOPLE 10
typedef struct
{
char name[NAME_SIZE];
char ssn[SSN_SIZE];
int yearOfBirth;
} person_t;
First of all, it seems to be pointers and references task. You may need to read this to understand them. In other words, cannot convert person_t to person_t* means you are trying to use your object person instead of reference to that specific person. * means reference, so you need to pass an address to it using &. Im not best explainer, check out the link instead and all answers, not only accepted one.
Code seems quite messy, I tried to fix it to compilable code, although I dont have C compiler (you may need to edit/fix according to your homework details):
#include <stdio.h>
#include <string.h>
#define NAME_SIZE 80
#define SSN_SIZE 13
#define NUM_PEOPLE 10
typedef struct
{
char name[NAME_SIZE];
char ssn[SSN_SIZE];
int yearOfBirth;
} person_t;
int main()
{
person_t people[NUM_PEOPLE];
printf("Get people\n");
getPeople(&people, 3);
printf("\nPrint people\n");
printPeople(people, 3);
return 0;
}
void getOnePerson(person_t *person)
{
printf("Enter full name: ");
scanf("%s", person -> name);
printf("\nEnter ssn: ");
scanf("%s", person -> ssn);
printf("\nEnter year of birth: ");
scanf("%s", person -> yearOfBirth);
}
void printOnePerson(person_t p)
{
printf("%s:%s:%d\n", p.name, p.ssn, p.yearOfBirth);
}
void getPeople(person_t *person[], int num)
{
int i;
for(i=0; i<num; i++)
{
getOnePerson(&person[i]);
}
}
void printPeople(person_t person[], int num)
{
int i;
for(i=0; i<num; i++)
{
printOnePerson(person[i]);
}
}
So, briefly, your getPeople(person_t *person[], int num) function's first parameter is person_t *person[], therefore you need to pass a &people. Same as getOnePerson(person_t *person) parameter person_t *person means you need to pass address to a single person object &person[i]. The meaning behind them that using references, you can edit the values in these objects directly in the function. While printPeople(person_t person[], int num) and printOnePerson(person_t p) are used for reading (not editing) thereby you can pass values themselves.
You have such a large number of small problems, it is difficult to know where to begin. First a nit, you never include spaces around "->" when referencing a structure member. Use p->name, not p -> name. Continuing...
You fail to validate the return of scanf. You must check the return Every Time, or you are tempting Undefined Behavior. You also must change "%99[^\n]" to " %79[^\n]" because neither "%c" or "%[...]" consume leading whitespace. Failing to add the " " before %12[^\n] would make it impossible to read p->ssn and lead to a matching failure reading p->yearOfBirth.
Note the change from 99 to 79. You #define NAME_SIZE 80 and declare char name[NAME_SIZE];, what do you think you are doing using a field-width modifier of 99 when at most 79 characters can be stored in name? (You have the same problem with #define SSN_SIZE 13). You use the field-width modifier with scanf to protect your array bounds. Setting the *field-width modifier greater than your array size (-1) removes the protection it should provide altogether.
Your failure to check the return of scanf and handle the three cases of return necessary will lead to Undefined Behavior if the user accidentally makes a single error in input. Failure to check the return of scanf is one of the most common pitfall new C programmer fall into. It is mandatory for every user input. Otherwise, you can have no confidence your code is actually processing valid data.
scanf can be used, if used correctly. This means you are responsible for checking the return of scanf every time. You must handle three conditions
(return == EOF) the user canceled input by generating a manual EOF by pressing Ctrl+d (or on windows Ctrl+z, but see CTRL+Z does not generate EOF in Windows 10 (early versions));
(return < expected No. of conversions) a matching or input failure occurred. For a matching failure you must account for every character left in your input buffer. (scan forward in the input buffer reading and discarding characters until a '\n' or EOF is found); and finally
(return == expected No. of conversions) indicating a successful read -- it is then up to you to check whether the input meets any additional criteria (e.g. positive integer, positive floating-point, within a needed range, etc..).
A short function implementation to empty all remaining characters in stdin in the event of matching failure could be as simple as:
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
(implementing in your code is left as an exercise for you)
Further, using type void as the return of an input function makes no sense. You must choose your return to provide the return of required information AND provide an indication of whether the input succeeded or failed. Using void for getOnePerson() means you have no way of knowing whether you received all valid input, or just received name, but not ssn, or if the user simply generated a manual EOF canceling input at each prompt. A simple integer return is all you need (e.g. return 0; on failure or return 1; only after all 3-inputs are validated) You could do something like:
int getOnePerson (person_t *p)
{
int rtn; /* scanf return */
/* validate each input for all 3 cases */
fputs ("\nEnter full name: ", stdout); /* no need for printf, no conversion */
if ((rtn = scanf (" %79[^\n]", p->name)) != 1) {
if (rtn == EOF)
puts ("(input complete)");
else
fputs ("error: invalid format 'p->name'.\n", stderr);
return 0;
}
/* validate each input for all 3 cases */
fputs ("Enter ssn: ", stdout); /* ditto */
if ((rtn = scanf (" %12[^\n]", p->ssn)) != 1) { /* " */
if (rtn != EOF)
fputs ("error: invalid format 'p->ssn'.\n", stderr);
return 0;
}
/* validate each input for all 3 cases */
fputs ("Enter year of birth: ", stdout);
if ((rtn = scanf ("%d", &p->yearOfBirth)) != 1) {
if (rtn != EOF)
fputs ("error: invalid format 'p->yearOfBirth'.\n", stderr);
return 0;
}
return 1; /* indicates all 3 input successfully received */
}
(note: input is complete when EOF is encountered, either manually generated by the user or encountered in the input stream)
void is also meaningless as a return for getPeople(). You can't use a for loop and just assume all inputs were successful, instead, you need to take input only while input is available, while protecting your array bounds, and then return the number of input actually received (which may be less than NUM_PEOPLE). Further, choose your type properly. For counters, size_t is the proper type (you can't have a negative number of persons), e.g.
size_t getPeople (person_t *p, size_t numOfPeople)
{
// for(int i = 0; i < sizeof(p); i++)
// {
// getOnePerson(p[i]);
// }
size_t n = 0;
while (n < numOfPeople && getOnePerson (&p[n]))
n++;
return n;
}
When you pass an array as a parameter to a function, the array is converted to a pointer to the first element. So when you do sizeof(p) within a function -- that is not what you want and does not provide the number of elements in the array referenced by p -- what it does provide is sizeof(a_pointer), which is fixed by your compiler (e.g. 8-bytes on x86_64, 4-bytes on x86). You pass numOfPeople -- use it, e.g.
void printPeople (person_t *p, size_t numOfPeople)
{
puts ("\nStored People\n");
// for(int i = 0; i < sizeof(p); i++)
for (size_t i = 0; i < numOfPeople; i++)
{
printOnePerson(p[i]);
}
}
You will also want to fix printf("%s\n", p.yearOfBirth); (yearOfBirth is not a string...)
Your header is fine, but it is missing something. Always include header guards around the content of your header files to prevent multiple inclusions of the file, e.g.
#ifndef mystructures_h
#define mystructures_h 1
...
/* your header content */
...
#endif
(note: the 1 isn't required, but if you are defining a constant, it is never a bad idea to give it an affirmative value of your choosing)
There are probably more that were corrected, but those were the major points. Putting it altogether, you could do:
structures.h
#ifndef mystructures_h
#define mystructures_h 1
#include <stdio.h>
#define NAME_SIZE 80
#define SSN_SIZE 13
#define NUM_PEOPLE 10
typedef struct {
char name[NAME_SIZE];
char ssn[SSN_SIZE];
int yearOfBirth;
} person_t;
size_t getPeople (person_t *p, size_t numOfPeople);
void printPeople (person_t *p, size_t numOfPeople);
#endif
(can you figure out why #include <stdio.h> was moved from structures.c into structures.h? do you know why the function prototypes for getPeople() and printPeople() are required in the header and not the rest?)
structures.c
#include "structures.h"
int getOnePerson (person_t *p)
{
int rtn; /* scanf return */
fputs ("\nEnter full name: ", stdout);
if ((rtn = scanf (" %79[^\n]", p->name)) != 1) {
if (rtn == EOF)
puts ("(input complete)");
else
fputs ("error: invalid format 'p->name'.\n", stderr);
return 0;
}
fputs ("Enter ssn: ", stdout); /* ditto */
if ((rtn = scanf (" %12[^\n]", p->ssn)) != 1) { /* " */
if (rtn != EOF)
fputs ("error: invalid format 'p->ssn'.\n", stderr);
return 0;
}
fputs ("Enter year of birth: ", stdout);
if ((rtn = scanf ("%d", &p->yearOfBirth)) != 1) {
if (rtn != EOF)
fputs ("error: invalid format 'p->yearOfBirth'.\n", stderr);
return 0;
}
return 1;
}
size_t getPeople (person_t *p, size_t numOfPeople)
{
// for(int i = 0; i < sizeof(p); i++)
// {
// getOnePerson(p[i]);
// }
size_t n = 0;
while (n < numOfPeople && getOnePerson (&p[n]))
n++;
return n;
}
void printOnePerson (person_t p)
{
printf("%s:", p.name);
printf("%s:", p.ssn);
// printf("%s\n", p.yearOfBirth);
printf("%d\n", p.yearOfBirth);
}
void printPeople (person_t *p, size_t numOfPeople)
{
puts ("\nStored People\n");
// for(int i = 0; i < sizeof(p); i++)
for (size_t i = 0; i < numOfPeople; i++)
{
printOnePerson(p[i]);
}
}
A short test program peopletest.c
#include "structures.h"
int main (void) {
person_t people[NUM_PEOPLE] = {{ .name = "" }};
size_t npeople = getPeople (people, NUM_PEOPLE);
printPeople (people, npeople);
}
Example Use/Output
$ ./bin/peopletest
Enter full name: Person A. One
Enter ssn: 123456789
Enter year of birth: 2001
Enter full name: Person B. Two
Enter ssn: 234567890
Enter year of birth: 2002
Enter full name: Person C. Three
Enter ssn: 345678901
Enter year of birth: 2003
Enter full name: (input complete)
Stored People
Person A. One:123456789:2001
Person B. Two:234567890:2002
Person C. Three:345678901:2003
Look things over and let me know if you have further questions.
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/
I created a program to make a diamond out of *'s. I am looking for a way to check if the type of input is an integer in the C language. If the input is not an integer I would like it to print a message.
This is what I have thus far:
if(scanf("%i", &n) != 1)
printf("must enter integer");
However it does not display the message if it's not an integer. Any help/guidance with this issue would be greatly appreciated!
you can scan your input in a string then check its characters one by one, this example displays result :
0 if it's not digit
1 if it is digit
you can play with it to make your desired output
char n[10];
int i=0;
scanf("%s", n);
while(n[i] != '\0')
{
printf("%d", isdigit(n[i]));
i++;
}
Example:
#include <stdio.h>
#include <string.h>
main()
{
char n[10];
int i=0, flag=1;
scanf("%s", n);
while(n[i] != '\0'){
flag = isdigit(n[i]);
if (!flag) break;
i++;
}
if(flag)
{
i=atoi(n);
printf("%d", i);
}
else
{
printf("it's not integer");
}
}
Use fgets() followed by strtol() or sscanf(..."%d"...).
Robust code needs to handle IO and parsing issues. IMO, these are best done separately.
char buf[50];
fgets(buf, sizeof buf, stdin);
int n;
int end = 0; // use to note end of scanning and catch trailing junk
if (sscanf(buf, "%d %n", &n, &end) != 1 || buf[end] != '\0') {
printf("must enter integer");
}
else {
good_input(n);
}
Note:
strtol() is a better approach, but a few more steps are needed. Example
Additional error checks include testing the result of fgets() and insuring the range of n is reasonable for the code.
Note:
Avoid mixing fgets() and scanf() in the same code.
{ I said scanf() here and not sscanf(). }
Recommend not to use scanf() at all.
strtol
The returned endPtr will point past the last character used in the conversion.
Though this does require using something like fgets to retrieve the input string.
Personal preference is that scanf is for machine generated input not human generated.
Try adding
fflush(stdout);
after the printf. Alternatively, have the printf output a string ending in \n.
Assuming this has been done, the code you've posted actually would display the message if and only if an integer was not entered. You don't need to replace this line with fgets or anything.
If it really seems to be not working as you expect, the problem must be elsewhere. For example, perhaps there are characters left in the buffer from input prior to this line. Please post a complete program that shows the problem, along with the input you gave.
Try:
#include <stdio.h>
#define MAX_LEN 64
int main(void)
{ bool act = true;
char input_string[MAX_LEN]; /* character array to store the string */
int i;
printf("Enter a string:\n");
fgets(input_string,sizeof(input_string),stdin); /* read the string */
/* print the string by printing each element of the array */
for(i=0; input_string[i] != 10; i++) // \0 = 10 = new line feed
{ //the number in each digits can be only 0-9.[ASCII 48-57]
if (input_string[i] >= 48 and input_string[i] <= 57)
continue;
else //must include newline feed
{ act = false; //0
break;
}
}
if (act == false)
printf("\nTHIS IS NOT INTEGER!");
else
printf("\nTHIS IS INTEGER");
return 0;
}
[===>] First we received input using fgets.Then it's will start pulling each digits out from input(starting from digits 0) to check whether it's number 0-9 or not[ASCII 48-57],if it successful looping and non is characters -- boolean variable 'act' still remain true.Thus returning it's integer.