Loop back after wrong input, problem with fgetc(stdin) and loop - c

So i have this simple code that i've found here. I want to make it work in a loop. I've tried couple of times with diffrent methods but output is bad. Working in Ubuntu Visual Studio
EDIT I've added if(y>=2015 && y<=3000) and it is working properly?
EDIT 2 I've modified my code and followed #Sergey advice... It is still not working properly...
I've tried to add "Check return value of scanf" but it is also not working.
if ((scanf("%u", &d) == 1) && (scanf("%u", &m) == 1) && (scanf("%u", &y) == 1))
while (fdateCheck());
else
//...EOF or conversion failure...
while (fdateCheck());
or
while ((rc = scanf("%u.%u.%u", &d, &m, &y)) != EOF)
{
if (rc != 3)
//...oops data problems...
else
//...all OK...
}
Need advice about checking return of scanfs
int fdateCheck();
unsigned int d,m,y;
unsigned int daysinmonth[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int rc;
int legit = 0;
int main()
{
printf("Enter the date\n");
scanf("%u.%u.%u",&d,&m,&y);
while (fdateCheck());
}
int fdateCheck()
{
if (y % 400 == 0 || (y % 100 != 0 && y % 4 == 0))
{
daysinmonth[1]=29;
}
else if (y >= 2015 && y <= 3000)
{
if (m < 13)
{
if (d <= daysinmonth[m-1])
legit = 1;
}
if (legit == 1)
{
system("clear");
printf("It is a legitimate date!\n");
return 0;
}
}
else
system("clear");
int ch = fgetc(stdin);
if (ch == '\n')
{
system("clear");
printf("It's not a legitimate date!\n");
printf("\nRetry: ");
return 1;
}
fflush(stdin);
}

If you have an appropriate code in the function void main() and need to repeat it in a cycle as you describe you can do the following:
rename original void main() to int one_step()
put return 0; after printf("It is a legitimate date!\n");
put return 1; after printf("It's not a legitimate date!\n");
create the new void main() function as:
void main() {
while(one_step());
}
You do not need break; and return main();. You may need some enhancements as described in the comments above. Good luck in coding!
UPDATE #1
I meant such an improvement of your code:
#include <stdio.h>
#include <stdlib.h>
int fdateCheck();
int main()
{
while (fdateCheck());
}
int fdateCheck()
{
int res;
int legit = 0;
unsigned int d,m,y;
unsigned int daysinmonth[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
// get input block
printf("Enter the date\n"); // prompt for data
res = scanf("%u.%u.%u",&d,&m,&y); // get input data
if (res == EOF) { // check if no more input
printf("Input error\n");
exit(1);
}
// check if input data is valid
if (res != 3) { // check if 3 numbers scanned
system("clear");
printf("It is not a date\n");
fflush(stdin);
return 1;
}
// make leap year correction
if (y % 400 == 0 || (y % 100 != 0 && y % 4 == 0)) {
daysinmonth[1]=29; // leap year correction
}
// check if year, month and day is valid
if (y >= 2015 && y <= 3000) { // check if year in this range
if (0 < m && m < 13) { // check if month in this range
if (0 < d && d <= daysinmonth[m-1]) // check if day in this range
legit = 1;
}
}
// print a message and finish the iteration
if (legit == 1) {
printf("It is a legitimate date!\n");
return 0;
}
else {
system("clear");
printf("It's not a legitimate date!\n");
printf("Please retry.\n");
fflush(stdin);
return 1;
}
}
I also moved all variables inside the function. This will restore values of legit and daysinmonth[1] before each iteration.
UPDATE #2
My next proposal:
#include <stdio.h>
#include <stdlib.h>
int fdateCheck();
int main()
{
while (fdateCheck());
}
int fdateCheck()
{
int res;
int legit = 0;
unsigned int d,m,y;
unsigned int daysinmonth[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
// get input block
printf("Enter the date\n"); // prompt for data
res = scanf("%u.%u.%u",&d,&m,&y); // get input data
if (res == EOF) { // check if no more input
printf("Input error\n");
exit(1);
}
// check if input data is valid
if (res != 3) { // check if 3 numbers scanned
fgetc(stdin); // remove a barrier
system("clear");
printf("It is not a date\n");
fflush(stdin);
return 1;
}
// make leap year correction
if (y % 400 == 0 || (y % 100 != 0 && y % 4 == 0)) {
daysinmonth[1]=29; // leap year correction
}
// check if year, month and day is valid
if (y >= 2015 && y <= 3000) { // check if year in this range
if (0 < m && m < 13) { // check if month in this range
if (0 < d && d <= daysinmonth[m-1]) // check if day in this range
legit = 1;
}
}
// print a message and finish the iteration
if (legit == 1) {
printf("It is a legitimate date!\n");
return 0;
}
else {
system("clear");
printf("It's not a legitimate date!\n");
printf("Please retry.\n");
fflush(stdin);
return 1;
}
}

Related

How do you check if date is in correct format

For example in input.txt file there is date 20210405. Date is in rrrrmmdd format without any '/' or '.'. How do you check if date is in correct format not in 20211405?.
My code works only for numbers that dont have zero in day for example 05,07,08, my code works only for 10 or 11. How do I fix that?
int main() {
char load[50];
long year, day, month;
int lenght, lenght2, lenght3;
int i = 0;
FILE *file;
file = fopen("input.txt", "r");
while (fgets(load, sizeof load, file) != NULL) {
if (i == 0) {
if (strlen(load) == 8) {
year = strtol(load, NULL, 10);
month = year;
day = year;
year = year * 0.0001;
lenght = (log10(abs(year))) + 1;
if (lenght == 4) {
day = day % 100;
lenght2 = (log10(abs(day))) + 1;
if (lenght2 == 2 && day <=31) {
month = (month % 10000) - 30;
month = month / 100;
lenght3 = (log10(abs(day))) + 1;
if (month <= 12 && lenght2 == 2) {
printf("Datum: %s", load);
} else {
printf("Invalid input.");
}
} else {
printf("Invalid input.");
}
} else {
printf("Invalid input.");
}
} else {
printf("Invalid input.");
}
}
}
}
You have a few flaws in your code:
fgets includes a \n at the end of your string if it is found in the file. Unless you read the last line where no more \n is present, you will get 9 characters in your buffer.
Your condition if (strlen(load) == 8) will fail in the other cases.
You should not use floating point operations on integers.
year = year * 0.0001; In best case it is identical to year = year / 10000;, in worst case you get some rounding errors.
You check the "length" of the value for day and month by taking log10. That means that the numerical value must be >10 to get a length of 2. That is exactly what you are complaining about.
A proper check for valid valued would be to check whether the numerical value is in proper range.
For some reason to reduce month by 30. That doesn't really make much sense.
You don't use variable i at all. The corresponding if (i==0) is rather useless.
A fixed version could look like this:
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
int main(void) {
char load[50];
long year, day, month;
bool valid = false;
FILE* file = fopen("input.txt", "r");
// TODO: Check for NULL
while (fgets(load, sizeof load, file) != NULL) {
// Remove trailing '\n'
int len = strlen(load);
if (load[len-1] == '\n') {
load[len-1] = 0;
}
// Do we have exactly 8 digits?
if (strlen(load) == 8) {
long value = strtol(load, NULL, 10);
year = value / 10000;
month = (value / 100) % 100;
day = value % 100;
if (year >= 1000) {
if (day > 0 && day <= 31) {
if (month > 0 && month <= 12) {
valid = true;
}
}
}
}
if (valid) {
printf("Datum: %s", load);
}
else {
printf("Invalid input.");
}
}
}
}
It is a bad idea to use floating point arithmetics to split the value into the year, month and day parts. The main reason for this is floating point numbers are represented internally in base 2 so 0.0001 is not represented exactly, so converting the multiplication result to long may cause truncation of the value.
You should instead use integer arithmetics this way:
long value = strtol(load, NULL, 10);
year = value / 10000;
month = (value / 100) % 100;
day = value % 100;
You could also use sscanf() to parse the line directly into 3 int values:
#include <ctype.h>
#include <stdio.h>
#include <string.h>
int main() {
char load[50];
int year, day, month;
int i = 0;
FILE *file = fopen("input.txt", "r");
if (file == NULL) {
fprintf(stderr, "cannot open input.txt: %s\n", strerror(errno));
return 1;
}
while (fgets(load, sizeof load, file) != NULL) {
if (i++ == 0) {
static const int mlen[] = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
char c[2];
/* accept up to 4 bytes for year, 2 for month, 2 for day and check for a trailing newline */
if (sscanf(load, "%4d%2d%2d%1[\n]", &year, &month, &day, c) != 4) {
fprintf(stderr, "invalid date line: %s", load);
break;
}
if (year < 1901 || year > 2099
|| month < 1 || month > 12
|| day < 1 || day > mlen[month]
|| (day == 29 && month == 2 && year % 4 != 0)) {
fprintf(stderr, "invalid date: %d/%d/%d\n", day, month, year);
break;
}
printf("Datum: %s", load);
} else {
/* handle other lines */
...
}
}
fclose(file);
return 0;
}

How to check the digit that input the date in form dd-mm-yyyy in C?

Can we check the date input form dd-mm-yyyy and also check how many digits that users input? Example the users input 7-7-2000, is Invalid, it must be 07-07-2000.
This is my code.
int dateCheck(int argc, const char * argv[]){
int day;
int month;
int year;
int count = 0;
char inputValue[20];
printf("Enter date : ");
fgets(inputValue, sizeof(inputValue), stdin);
sscanf(inputValue, "%d-%d-%d",&day,&month,&year);
if ((year >= 1919) && (year <= 2119)) {
if ((month >= 1) && (month <= 12)) {
if ( ((day >= 1) && (day <= 31)) && ((month == 1) || (month == 3) || (month == 5) || (month == 7) || (month == 9) || (month == 11)) ) {
printf("Valid.\n");
}
else if ( ((day >= 1) && (day <= 30)) && ((month == 4) || (month == 6) || (month == 8) || (month == 10) || (month == 12)) ){
printf("Valid.\n");
}
else if ( ((day >= 1) && (day <= 28)) && (month == 2) ){
printf("Valid.\n");
}
else if ( (day == 29) && (month == 2) && ( (year%400 == 0) || ((year%4 == 0) && (year%100 != 0)) ) ){
printf("Valid.\n");
}
else{
printf("Invalid.\n");
}
}
else{
printf("Invalid.\n");
}
}
else{
printf("Invalid.\n");
}
Can we check the date input form dd-mm-yyyy and also check how many digits that users input?
Sure, how about using some helper functions?
Divide the task in conceptual parts and write code for each. Divide and conquer.
// return 0: fail
// return 1: success
static int scan_int(int *dest, const char *s, int min, int max, int digits,
char endchar) {
*dest = 0;
for (int i = 0; i < digits; i++) {
if (*s < '0' || *s > '9')
return 0;
*dest = *dest * 10 + *s - '0';
s++;
}
return *dest >= min && *dest <= max && *s == endchar;
}
static int isleapyear(int year) {
if (year % 4) return 0;
if (year > 1582) { // see https://en.wikipedia.org/wiki/Gregorian_calendar
if (year % 100 == 0 && year % 400) return 0;
}
return 1;
}
static int eom(int year, int month) {
static char eoms[1 + 12] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (month != 2) {
return eoms[month];
}
return eoms[month] + isleapyear(year);
}
Now armed with some helper functions, the task is directly coded.
// dd-mm-yyyy
#define YEAR_MIN 1919
#define YEAR_MAX 2019
int dateCheck(void) {
int day, month, year;
char inputValue[20] = {0};
printf("Enter date : ");
fgets(inputValue, sizeof inputValue, stdin);
if (scan_int(&year, &inputValue[3 + 3], YEAR_MIN, YEAR_MAX, 4, '\0')
&& scan_int(&month, &inputValue[3], 1, 12, 2, '-')
&& scan_int(&day, inputValue, 1, eom(year, month), 2, '-')) {
printf("Valid. %02d-%02d-%04d\n", day, month, year);
return 1;
}
printf("Invalid.\n");
return 0;
}
Nifty trick: Code validates day after fetching a valid year, month.
Unless you are prohibited from using the functions provided in string.h, they can make you date check a bit easier. For example, you can verify that only allowable characters are present in the input using strchr(). You can also use strchr() to locate each '-' when checking that each dd mm yyyy group is the correct number of characters. You can use strlen() to verify the final yyyy group is 4-characters, and you can use strcspn() to trim the trailing '\n' at the end of any input read using fgets(). The rest is simple ariithmetic.
For example, you could write a chkdate(const char *s) function to check that the string s is in your desired format, returning 1 if it is, or 0 otherwise, e.g.:
#define DATEGRP 3 /* if you need a constant, #define one (or more) */
#define DATELN 10
#define DATEC 128
/* check if given date format is dd-mm-yyyy
* returns 1 on success, zero otherwise
*/
int chkdate (const char *s)
{
const char *datechars = "-0123456789", /* valid characters */
*p = s, /* pointer & end-pointer */
*ep = p;
size_t group = 0; /* group counter, dd mm yyyy */
int groups[] = { 2, 2, 4 }; /* req'd chars per group */
for (int i = 0; s[i]; i++) /* verify only datechars in s */
if (!strchr (datechars, s[i]))
return 0;
/* loop over dd mm groups using strchr to locate '-' */
while (group < DATEGRP - 1 && (ep = strchr(p, *datechars))) {
if ((ep - p) != groups[group]) /* verify correct no. of chars */
return 0;
p = ++ep; /* update p to following char */
group++; /* increment groups counter */
}
/* check chars in final group and all digits */
if (strlen (p) != (size_t)groups[group] || strchr (p, '-'))
return 0;
return 1; /* if you made it here, valid date, return 1 */
}
You can also make a trivial solution using sscanf alone, that behaves in the exact same manner simply by using a proper format string with appropriate field-width modifiers on each integer conversion checking the return of sscanf against 3, except the sscanf trivial solution will fail for any case where yyyy begins with an integer and then contains an invalid character) e.g.
int chkdate (const char *s)
{
int d, m ,y;
return sscanf (s, "%2d-%2d-%4d", &d, &m, &y) == 3;
}
Adding the required headers and a short example program that will validate user input using the function above you could do:
#include <stdio.h>
#include <string.h>
#define DATEGRP 3 /* if you need a constant, #define one (or more) */
#define DATELN 10
#define DATEC 128
/* check if given date format is dd-mm-yyyy
* returns 1 on success, zero otherwise
*/
int chkdate (const char *s)
{
const char *datechars = "-0123456789", /* valid characters */
*p = s, /* pointer & end-pointer */
*ep = p;
size_t group = 0; /* group counter, dd mm yyyy */
int groups[] = { 2, 2, 4 }; /* req'd chars per group */
for (int i = 0; s[i]; i++) /* verify only datechars in s */
if (!strchr (datechars, s[i]))
return 0;
/* loop over dd mm groups using strchr to locate '-' */
while (group < DATEGRP - 1 && (ep = strchr(p, *datechars))) {
if ((ep - p) != groups[group]) /* verify correct no. of chars */
return 0;
p = ++ep; /* update p to following char */
group++; /* increment groups counter */
}
/* check chars in final group and all digits */
if (strlen (p) != (size_t)groups[group] || strchr (p, '-'))
return 0;
return 1; /* if you made it here, valid date, return 1 */
}
int main (void) {
char buf[DATEC] = "";
size_t len;
fputs ("enter date (dd-mm-yyyy): ", stdout); /* prompt */
if (!fgets (buf, DATEC, stdin)) { /* read/validate input */
fputs ("(user canceled input)\n", stderr);
return 1;
}
buf[(len = strcspn(buf, "\n"))] = 0; /* trim trailing '\n', save len */
if (len != DATELN) { /* if not DATELN chars, invalid */
fputs ("error: invalid date format,\n", stderr);
return 1;
}
if (chkdate (buf)) /* check dd-mm-yyyy format */
puts ("date is valid");
else /* otherwise, invalid format */
fputs ("date is invalid.\n", stderr);
}
Example Use/Output
Wrong number of digits:
$ ./bin/chkdate
enter date (dd-mm-yyyy): 7-7-2000
error: invalid date format,
Invalid character 'a' included:
$ ./bin/chkdate
enter date (dd-mm-yyyy): 07-7a-2000
date is invalid.
Good date:
$ ./bin/chkdate
enter date (dd-mm-yyyy): 07-07-2000
date is valid
There are literally dozens of ways to approach this with many combinations of loops, counters, pointers, character classification, etc... There isn't any "right" way, so long as it is reasonably efficient and does the validation. So experiment, write it several different ways and choose the one that is most readable and understandable to you.
Here you can first take in the string, check it's format and then extract individual digits from the string.
#include<stdio.h>
#include<string.h>
void checkDate(){
char str[20];
printf("%s","Enter date : ");
fgets(str, sizeof(str), stdin);
//char str[11] = "32-02-2019";
int d1=str[0]-48; //Convert the ascii digits to integers
int d2=str[1]-48;
int day=d1*10+d2;
int m1=str[3]-48;
int m2=str[4]-48;
int month=m1*10+m2;
int y1=str[6]-48;
int y2=str[7]-48;
int y3=str[8]-48;
int y4=str[9]-48;
int year=y1*1000+y2*100+y3*10+y4;
int format_validity=0;
printf("stringLen:%ld\n",strlen(str));
if(strlen(str)==11){ //check length of the string
if(str[2]=='-' && str[5]=='-'){ //check the separator positions
if((d1>=0 && d1<=9) && (d2>=0 && d2<=9)){ //check digits are in range 0-9
if((m1>=0 && m1<=9) && (m2>=0 && m2<=9)){ //check digits are in range 0-9
if((y1>=0 && y1<=9) && (y2>=0 && y2<=9) && (y3>=0 && y3<=9) && (y4>=0 && y4<=9)){ //check the digit are in range 0-9
format_validity=1; //mark the format valid
}
}
}
}
}//else the format is invalid
if(format_validity){
//Your Code from here
if ((year >= 1919) && (year <= 2119)) {
if ((month >= 1) && (month <= 12)) {
if (((day >= 1) && (day <= 31)) && ((month == 1) || (month == 3) || (month == 5) || (month == 7) || (month == 9) || (month == 11)) ) {
printf("Valid.\n");
}
else if ( ((day >= 1) && (day <= 30)) && ((month == 4) || (month == 6) || (month == 8) || (month == 10) || (month == 12)) ){
printf("Valid.\n");
}
else if ( ((day >= 1) && (day <= 28)) && (month == 2) ){
printf("Valid.\n");
}
else if ( (day == 29) && (month == 2) && ( (year%400 == 0) || ((year%4 == 0) && (year%100 != 0)) ) ){
printf("Valid.\n");
}
else{
printf("Invalid.\n");
}
}
else{
printf("Invalid.\n");
}
}
else{
printf("Invalid.\n");
}
}else printf("Invalid format.\n");
printf("day:%d, month:%d, year:%d",d1*10+d2,m1*10+m2,year);
}
int main(){
checkDate();
return 0;
}
Test:
[1]
Enter date : 29-02-2019
stringLen:11
Invalid.
day:29, month:2, year:2019
[2]
Enter date : 29-02-2020
stringLen:11
Valid.
day:29, month:2, year:2020
[3]
Enter date : a0-03-2020
stringLen:11
Invalid format.
day:490, month:3, year:2020
[4]
Enter date : 12-2-2019
stringLen:10
Invalid format.
day:12, month:17, year:152

Verifying integer input in a union in C

I'm trying to verify that what a user inputs in to one section of a union is actually an integer. I have been trying to use the isdigit function (see below) but I am having no success. All I need to do is make sure the user enters ONLY numbers for the date, but I am having a lot of trouble with it.
My code:
#define STRSIZE 30
#define PROFSIZE 30
#define NBRASSI 2
#define TRUE 1
#define FALSE 0
struct assignment
{
char name[STRSIZE];
char prof[PROFSIZE];
int duedate;
float value;
};
.
.
.
struct assignment populate_structure(struct assignment assi[], int assi_nbr)
{
int count;
int date_check = FALSE;
for (count = 0; count < assi_nbr; count++)
{
flushall();
printf("Enter Assignment #%d name (max %d chars):\n", count + 1,
STRSIZE);
gets(assi[count].name);
while (date_check == FALSE)
{
printf("Enter due date for Assignment #%d (YYYYMMDD):\n",
count + 1);
scanf("%d", &assi[count].duedate);
if (isdigit(assi[count].duedate))
{
date_check = TRUE;
}
else
{
printf("Invalid");
date_check = FALSE;
}
}
printf("Enter %% of final grade for Assignment #%d:\n", count + 1);
scanf("%f", &assi[count].value);
flushall();
printf("Enter Professor's name for Assignment #%d (max %d chars):\n",
count + 1, PROFSIZE);
gets(assi[count].prof);
printf("\n\n");
}
return assi[count];
}
It gives me no errors in Visual Studio, but when I run the program I get an Abort error whenever ANY value is input for assi[].duedate
If I remove if (isdigit(assi[count].duedate)), the program runs fine (so long as they only input an integer for duedate). Any help is greatly appreciated.
As the commenters already noted: isdigit() is for single characters only. Also: the way you use scanf() already assures the result to be a number.
But you should read a string instead, it is easier to test. For example
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main(void)
{
int c;
int position = 0;
int res;
char date[9] = {'\0'};
char input[20] = {'\0'};
puts("try if date is in the format \"YYYYMMDD\"");
res = scanf("%19s",input);
if(res != 1){
fprintf(stderr,"input was a mess, aborting\n");
goto __FAILURE;
}
c = input[position];
while(c != '\0'){
switch(position){
case 0:
if( c == '2' || c == '1' ){
date[position] = (char) c;
} else {
goto __FAILURE;
}
break;
case 1:
if( c == '9' || c == '0' ){
// check valid digit by looking back here
// (not implemented)
date[position] = (char) c;
} else {
goto __FAILURE;
}
break;
case 2:
if( isdigit(c) ){
date[position] = (char) c;
} else {
goto __FAILURE;
}
break;
case 3:
if( isdigit(c) ){
date[position] = (char) c;
} else {
goto __FAILURE;
}
break;
case 4: // month
if( c == '0' || c == '1' ){
date[position] = (char) c;
} else {
goto __FAILURE;
}
break;
case 5: // month
if( isdigit(c) ){
// check valid digit by looking back here
// (not implemented)
date[position] = (char) c;
} else {
goto __FAILURE;
}
break;
case 6: // day
if( c == '0' || c == '1' || c == '2' || c == '3'){
date[position] = (char) c;
} else {
goto __FAILURE;
}
break;
case 7: // day
if( isdigit(c)){
// check valid digit by looking back here
// (not implemented)
date[position] = (char) c;
} else {
goto __FAILURE;
}
break;
default:
break;
}
c = input[++position];
}
printf("Date was correct and is %s\n",date);
exit(EXIT_SUCCESS);
__FAILURE:
printf("Date was wrong at position %d with character %c or just too short/long\n",position,c);
exit(EXIT_FAILURE);
}
Fill out the rest (check if date is valid and in the correct range). You could also part the date into individual year/month/day sections instead of putting the string into another string and convert them into numbers via atoi ( you can use atoi `because you already made sure to have a valid number) or by converting manually inside the switch without extra variables and functions. Checking ranges arithmetically is simpler than comparing strings/characters.
Here is my offering - any invalid entry calls exit(1) though a better error handler would be preferred. The data is entered as a string, and then extracted and validated.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
typedef struct {
int year;
int month;
int day;
} mydate_t;
int main(void){
int i;
mydate_t dat = {0};
int dayspermon[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
char str[12];
printf("Enter a date (YYYYMMDD): ");
fflush(stdout);
if(fgets(str, sizeof str, stdin) == NULL) {
exit(1);
}
for(i=0; i<8; i++) {
if(!isdigit(str[i])) {
exit(1); // also fails when the string is too short
}
}
// extract
for(i=0; i<4; i++) {
dat.year = dat.year * 10 + str[i] - '0';
}
for(i=4; i<6; i++) {
dat.month = dat.month * 10 + str[i] - '0';
}
for(i=6; i<8; i++) {
dat.day = dat.day * 10 + str[i] - '0';
}
// validate
if(dat.year < 2000 || dat.year > 3000) // arbitrary range
exit(1);
if(dat.year % 4 == 0 && (dat.year % 100 != 0 || dat.year % 400 == 0)) {
dayspermon[1]++; // leap year adjustment
}
if(dat.month < 1 || dat.month > 12)
exit(1);
if(dat.day < 1 || dat.day > dayspermon[dat.month - 1])
exit(1);
printf("Date: %04d %02d %02d\n", dat.year, dat.month, dat.day);
}

looping error ,program stop and show "INPUT NOT VALID!!" but ask again for input?

I have these two problems:
I have this code which will take number of hours. If total_hours is greater than 60, then it should stop. It does stop, but then it shows the messageINPUT NOT VALID!! and asks for input again (which it should not.)
Otherwise, is true the first input called "name" is not asked in the second time "when use put "Y" as an answer ".
Code:
#include <stdio.h>
#include <string.h>
int main() {
int total_hours;
char name[100], category[3], nic[140];
float gross_pay, overtime_pay, net_pay;
int straight_time = 44, pay_hour;
char con;
do {
fgets(name, 100, stdin);
printf("NIC:");
fgets(nic, 140, stdin);
printf("Category:");
fgets(category, 3, stdin);
printf("Total Hours:");
scanf("%d", & total_hours);
printf("\n\t\t Smart Store Hypermarket \n");
printf("============================================================\n");
printf("Name:%s\nNIC:%s\nCategory:%s\nTotal Hours:%d\n\n", name, nic, category, total_hours);
if (strncmp(category, "A1", 2) == 0) {
pay_hour = 5;
} else if (strncmp(category, "A2", 2) == 0) {
pay_hour = 7;
} else if (strncmp(category, "M1", 2) == 0) {
pay_hour = 10;
} else if (strncmp(category, "M2", 2) == 0) {
pay_hour = 15;
} else if (strncmp(category, "BB", 2) == 0) {
pay_hour = 20;
}
if (total_hours > 44 && total_hours < 60) {
gross_pay = straight_time * pay_hour;
overtime_pay = pay_hour;
net_pay = gross_pay + pay_hour;
printf("Gross pay = %.2f RM\nOvertime pay =%.2f RM\nNet pay= %.2f RM\n", gross_pay, overtime_pay, net_pay);
printf("\nContinue (Y/N) ? :");
scanf("\n%c", &con);
} else {
printf("INPUT NOT VALID!!");
}
} while ((con != 'N'));
getchar();
}
Put a break statement after the line printf("INPUT NOT VALID!!"); to exit the loop instead of repeating it.
As for your other issue, Running fgets after a scanf can cause it to receive an empty line. See this question for the reason and solutions.

A C program to check if the entered date is valid or not

I was asked to right a program which checks if the date entered by the user is legitimate or not in C. I tried writing it but I guess the logic isn't right.
//Legitimate date
#include <stdio.h>
void main()
{
int d,m,y,leap;
int legit = 0;
printf("Enter the date\n");
scanf("%i.%i.%i",&d,&m,&y);
if(y % 400 == 0 || (y % 100 != 0 && y % 4 == 0))
{leap=1;}
if (m<13)
{
if (m == 1 || (3 || ( 5 || ( 7 || ( 8 || ( 10 || ( 12 )))))))
{if (d <=31)
{legit=1;}}
else if (m == 4 || ( 6 || ( 9 || ( 11 ) ) ) )
{if (d <= 30)
{legit = 1;}}
else
{
if (leap == 1)
{if (d <= 29)
{legit = 1;}}
if (leap == 0)
{{if (d <= 28)
legit = 1;}}
}
}
if (legit==1)
printf("It is a legitimate date!\n");
else
printf("It's not a legitimate date!");
}
I am getting the correct output if the month has 31 days but for the rest of the months, the output is legitimate if the day is less than 32. Your help is appreciated!
i rewrite you program as simple and easy, i think this may help
//Legitimate date
#include <stdio.h>
void main()
{
int d,m,y;
int daysinmonth[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int legit = 0;
printf("Enter the date\n");
scanf("%i.%i.%i",&d,&m,&y);
// leap year checking, if ok add 29 days to february
if(y % 400 == 0 || (y % 100 != 0 && y % 4 == 0))
daysinmonth[1]=29;
// days in month checking
if (m<13)
{
if( d <= daysinmonth[m-1] )
legit=1;
}
if (legit==1)
printf("It is a legitimate date!\n");
else
printf("It's not a legitimate date!");
}
You can't chain conditionals like this:
if (m == 1 || (3 || ( 5 || ( 7 || ( 8 || ( 10 || ( 12 )))))))
Instead, you'll have to test each scenario specially:
if (m == 1 || m == 3 || m == 5 || ...)
Your version simply ORs the results of the first test (m == 1) with the value of 3, which in C is a non-zero and therefore always a boolean true.
This test is certainly wrong:
if (m == 1 || (3 || ( 5 || ( 7 || ( 8 || ( 10 || ( 12 )))))))
This must be
if ((m == 1) || (m == 3) || (m == 5) || ... )
Performing a logical or with a non-zero expression will always evaluate to true. Therefore, your entire test will always be true.
You can check date legitimacy simpler:
#define _XOPEN_SOURCE 600
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
time_t get_date(char *line){
#define WRONG() do{printf("Wrong date!\n"); return -1;}while(0)
time_t date;
struct tm time_, time_now, *gmt;
time_.tm_sec = 0;
time_.tm_hour = 0;
time_.tm_min = 0;
if(strchr(line, '.') && sscanf(line, "%d.%d.%d", &time_.tm_mday, &time_.tm_mon, &time_.tm_year) == 3){
time_.tm_mon--; time_.tm_year += (time_.tm_year < 100) ? 100 : -1900;
}else
WRONG();
memcpy(&time_now, &time_, sizeof(struct tm));
date = mktime(&time_now);
gmt = localtime(&date);
if(time_.tm_mday != gmt->tm_mday) WRONG();
if(time_.tm_mon != gmt->tm_mon) WRONG();
if(time_.tm_year != gmt->tm_year) WRONG();
date = mktime(&time_);
return date;
#undef WRONG
}
int main(int argc, char** argv){
struct tm *tmp;
if(argc != 2) return 1;
time_t GD = get_date(argv[1]);
if(GD == -1) return -1;
printf("Int date = %d\n", GD);
printf("your date: %s\n", ctime(&GD));
return 0;
}
//reading date and checking if valid or not
//firstly we will check the yeear then the month and then the date
//
//
//
//
#include<stdio.h>
int main()
{
int d,m,y;
printf("ENTER THE DATE IN DD/MM/YYYY FORMAT:");
scanf("%d%d%d",&d,&m,&y);
//check year
if(y>0 && y<9999)
{
// check month
if(m>=1 && m<=12)
{
if((d>=1 && d<=31) && (m==1 || m==3 || m==5 || m==7 || m==8 || m==10 || m==12))
printf("the date is valid in a month with 31 days:");
else if ((d>=1 && d<=30) && (m==4 || m==6 || m==9 || m==11 ))
printf("the date is valid in a feb with 30 days:");
else if ((d>=1 && d<=29) && (m==2) && ((y%400==0) || (y%4==0) && (y%100!=0)))
printf("the date is valid in feb of a leap year:");
else if ((d>=1 && d<=28) && (m==2) && (y%4==0) && (y%100==0))
printf("the date is valid in feb of a leap year:");
else if ((d>=1 && d<=28) && (m==2) && (y%4!=0) )
printf("the date is valid in feb of a non leap year:");
else
printf("the date is invalid:");
}
else
{
printf("the month is not valid:");
}
}
else
{
printf("the date is not valid:");
}
return 0;
}

Resources