#include <iostream>
#include <conio.h>
using namespace std;
void pause()
{
system("pause");
}
/* Hàm nhập ngày tháng năm sinh dạng d(d)/m(m)/yy(yy)
Với 3 tham số đầu vào là số nguyên (ngày, tháng năm)
Nếu tại 1 thời điểm mà nhập sai, thì không cho nhập tiếp.
Ví dụ, chưa có ký tự nào mà nhập / thì trên màn hình ko hiện gì cả
không được nhập 2 dấu // gần nhau và bắt buộc có 2 / trong dãy
Nếu nhập 2 ký tự đầu là số vd 12. thì ký tự tiếp theo bắt buộc phải là dấu /. vì đâu có ngày 123 (3 chữ số)
Nếu ký tự đầu tiên là 4 trở lên thì ký tự thứ 2 chỉ được nhập / chứ k cho nhập số. vd 4/ chứ k thể nhập 41 vì làm gì có ngày 41
Nếu ký tự đầu là 3 thì ký tự thứ 2 lớn hơn thì ko có hiện tưởng gì trên màn hình hết (y như các phím bị vô hiệu hóa vậy)
Nếu chưa nhập xong, hoặc năm=0 hoặc nhập ngày 29/2 năm ko nhuận thì nhấn enter không có tác dụng
Nếu chuỗi đang là 31/0 thì không cho nhập tiếp số 0 hoặc / hoặc các số tháng ko phù hợp (->> 31/00 hay 31/0/ là sai cú pháp, còn 31/02 hay 31/04 thì sai vì tháng 2 và tháng 4 không có 30 ngày)
Nói chung, phải bắt buộc người dùng nhập đúng cú pháp và đúng ngày tháng (vd đã nhập ngày 31 thì không thể nhập tháng 11)
Nếu người dùng cố tình nhập sai thì trên màn hình chẳng hiện gì hết, trạng thái vẫn y như cũ
ví dụ trên màn hình đang là "12/" nếu người dùng nhập / (để thành 12// ) thì không được gì hết
Khi đó trên màn hình vẫn là "12/"
Hoặc nếu trên màn hình đang là "31/" . Nếu người dùng nhập số 6 (để thành 31/6) thì cũng ko có hiện tượng gì, trên màn hình vẫn hiện "31/"
Khi ngày tháng đã hợp lệ, nếu người dùng nhấn Ok thì
Hàm trả về 3 số nguyên ngày, tháng, năm
*/
bool namnhuan (int n) // hàm kiểm tra năm nhuận
{
if ((n%4==0) && ( (n%100!=0) || (n%400==0) ) ) return 1;
return 0;
}
void indate (int &a, int &b, int &c)
{
int n,xoec=0, index=0; // index là chỉ số mảng s, xoec là biến đếm số ký tự / n là biến để lấy mã ascii
a=b=c=0; // a = ngày, b= tháng, c= năm
char s[30]; // s lưu chuỗi ngày tháng đang nhập trong quá trình thực hiện
do
{
//cout <<"\n\n A= " <<a <<", b=" <<b <<" c=" <<c <<", xoec = " <<xoec <<", index=" <<index <<", s=" <<s <<"\n\n";
loop:; // đặt mốc lặp lại, dùng cho goto
n=getch(); // lấy mã ascii
if (n>='/' && n<='9' &&index<10) // nếu nằm trong các kí tự cho phép
{
if (index==0) // nếu là kí tự đầu tiên
{
if (n=='/'); // kí tự đầu ko thể là dấu cách
else // ngược lại
{
cout <<char(n
); // xuất ra a=n-48; // lưu vào a
s[index++]=char(n); // lưu vào s
}
}
else if (index==1) // nếu là ký tự thứ 2
{
if (s[index-1]>'3' && n!='/'); // nếu kí tự đầu lớn hơn 3 thì ko cho nhập số nữa, chỉ co nhập / (ví dụ không thể nhập thành ngày 40 or 41, mà phải 4/)
else if (n>'1' && s[index-1]=='3'); // nếu ký tự đầu là 3 thì chỉ được nhập 0 và 1 (thành 30, 31, không thể nhập 32,33...)
else // ngược lại
{
if ( (n=='/' || n=='0') && s[index-1]=='0'); // nếu ký tự đầu là 0 thì k thể nhập tiếp số 0 hoặc / (ví dụ 00/ hay 0/)
else
{
if (n=='/') xoec++; // nếu là xoec thì tăng xoec 1 (5/ - 7/ - 2/)
else a=10*a+n-48; // là số thì đưa vào a (23 -> a=23)
cout <<char(n
); // xuất ra màn hình, dù là xoec hay số s[index++]=char(n); // đưa vào s dù là xoec hay số
}
}
}
else if (index==2) // nếu ký tự thứ 3
{
if (xoec==0 && n!='/'); // nếu chưa có dấu xoec nào thì bắt buộc phải nhập xoec, vì ngày chỉ tối đa 2 ký tự (vd: 12 không thể nhập số tiếp thành 121 mà phải 12/)
else if (xoec==1 && n=='/'); // nếu là xoec, nhưng đã có (1/ thì không thể nhập / nữa)
else
{
if (n=='/' && xoec==0) xoec++; // nếu là / thì tăng xoec (10/ -> xoec=1)
else b=n-48; // nếu là số thì tăng b. vd 3/1 ->> a=3, b=1
cout <<char(n
); // xuất ra s[index++]=char(n); // đưa vào s
}
}
else if (index==3) // nếu là ký tự thứ 4
{
if (s[1]=='/' && s[2]>'1' && n!='/'); // nếu có dạng sau: 3/2 thì không cho nhập số nữa, chỉ cho nhập dấu / vì nếu ->> 3/21 thì làm gì có tháng 21
else if (n=='/' && s[index-1]=='/'); // nếu có dạng 10/ thì không được nhập / nữa
else if (s[1]=='/' && s[2]=='1' && n>='3'); // nếu có dạng 4/1 thì chỉ đươc nhập tiếp số 2 trở xuống vì nếu nhập 3 trở lên ->> 4/13 mà đâu có tháng 13
else if (a==31 && (n=='2' || n=='4' || n=='6' || n=='9')); // nếu dạng 31/ thì không được nhập 2,4,6,9 vì các tháng đó đâu có ngày 31 (31/2, 31/4)..
else if (a==30 && n=='2'); // không được nhập ngày 30/2
else if (xoec==1 && s[1]=='/' && s[index-1]=='0' && (n=='0' || n=='/')); // nếu dạng 3/0 thì không cho nhập tiếp số 0 or / vd: (3/00 or 3/0/ là sai)
else
{
if (n=='/') xoec++;
else b=10*b+n-48; // lưu vào b. vd 1/10 ->> b=10. hoặc 12/3 ->> b=3
s[index++]=char(n);
}
}
else if (index==4) // nếu là ký tự thứ 5
{
if (xoec==1 && s[1]=='/' && n!='/'); // nếu dạng: 8/12 không cho nhập tiếp số, bắt phải nhập /
else if (n=='/' && s[index-1]=='/'); // nếu dạng 3/6/ không cho nhập / nữa vì có / trước nó
else if (s[2]=='/' && s[index-1]>'1' && n!='/'); // 11/2 không cho nhập thêm số thành 11/22 vì lố tháng. nhưng cho nhập / thành 11/2/
else if (s[2]=='/' && s[index-1]=='1' && n>'2'); // 11/1 chỉ cho phép nhập thành 11/10 or 11/11 or 11/12
else if (xoec==1 && s[2]=='/' && s[index-1]=='0' && (n=='0' || n=='/')); // nếu 10/0 thì k cho nhập thêm 0 hoặc / để thành 10/00 hoặc 10/0/
else if (a==31 && s[index-1]=='0' && (n=='2' || n=='4' || n=='6' || n=='9')); // không được nhập các tháng có tối đa 30 ngày, ví dụ 31/0 thì ko thể nhập tiếp 2 or 4..
else if (a==31 && s[index-1]=='1' && n=='1'); // nếu dạng 31/1 thì ko được nhập số 1. vì 31/11 ->> ko có ngày 31/11
else if (a==30 && s[index-1]=='0' && n=='2'); // tương tự áp dụng cho tháng 2
else
{
if (n=='/') xoec++; // nếu 12/1/ thì xoec tăng
else if (xoec<2) b=10*b+n-48; // 10/11 ->> lưu vào b, b thành 11.
else c=n-48; // 1/1/2 thì lưu 2 vào c ->> c==2
s[index++]=char(n);
}
}
else if (index==5) // nếu là ký tự thứ 6
{
if (xoec==1 && s[2]=='/' && n!='/'); // 11/11 không cho nhập số nữa, bắt nhập dấu /
else if (n=='/' && s[index-1]=='/'); // 1/12/ không cho nhập / nữa bắt buộc nhập số
else if (n=='/' && xoec==2); // 3/6/2 không cho nhập / nữa
else
{
if (n=='/') xoec++; // 11/11/ thì tăng xoec
else c=10*c+n-48; // nếu 1/12/3 thì c=3. hoặc nếu 1/2/34 thì c=34
s[index++]=char(n);
}
}
else // nếu ký tự thứ 7 trở lên. chắc chắn những ký tự này thuộc phần năm
{
if (n=='/'); // cho nên không cho nhập / nữa
else if (xoec==2 && (index-5!=1) && (index-5!=2) && s[index-5]=='/'); // nếu 1/1/1111 hoặc 11/1/1111 hoặc 1/11/1111 hoặc 11/11/1111 thì không cho nhập nữa vì năm đã có 4 ký tự.
else // khi đó tồn tại 2 / và / thứ 2 nằm cách index 5 ký tự
{
c=10*c+n-48; // tăng c
s[index++]=char(n);
}
}
}
else if (n=='\b' && index>0) // nếu là backspace
{
if (s[index-1]=='/') xoec--; // nếu là back ký tự / thì giảm xoec
if (xoec==2) c/=10; // khi xoec=2 thì index đang trong phạm vi của năm, nên giảm c
else if (xoec==1 && s[index-1]!='/') b/=10; // nếu xoec=1 mà ký tự back ko phải là / thì giảm b. vì nếu ký tự back là / mà giảm b là sai. vd: khi 12/12/ lúc này b=12 và xoec=2. nếu back 1 cái thì xoec còn 1, và b bị giảm còn 1 (nhưng thực chất b vẫn là 12)
else if (xoec==0 && s[index-1]!='/') a/=10; // giảm a, tương tự như b ở trên
s[--index]=0; // xóa ký tự cuối trong s
}
else if ( (n==13 && c==0) || (n==13 && a==29 && b==2 && !namnhuan(c)) ) goto loop; // nếu nhập 29/2 năm không nhuận hoặc chưa nhập đến năm thì k cho kết thúc
}
while (n!=13);
}
void main ()
{
int ngay, thang, nam;
indate (ngay,thang,nam);
cout <<"\n\nBan vua nhap: " <<ngay
<<"/" <<thang
<<"/" <<nam
;
pause();
}