Index |
||
DOSカレンダー
昔勉強のために作ったカレンダーです。単にひと月のカレンダーを表示しようというだけの単純なプログラムです。コンパイラはBorland C++ Compiler(BC++)です。
![]() sasaraan programming ![]() Exposition [1] Calendar クラス
作成するファイルは、ヘッダファイルcalendar.hと、ソースファイルcalendar.cppの二つだけです。流れとしては、
@ ユーザー入力を受け付ける ... GetDate関数 A 月の日数を求める ... GetDayCount 関数 (2月の時はうるう年かどうか調べる ... IsLeapYear 関数) B 月初の曜日を求める ... GetWeekDay 関数 C カレンダーを表示 ... SetCalendar 関数 という具合です。 Floor関数は曜日の算出の時に使います。Calendarクラスをつくってそれぞれの関数を定義します。ユーザー入力を GetDate関数で受け、カレンダーの作成と表示はSetCalendar関数にまとめることにします。内部処理で済むものは、protectedで宣言しています。
( ヘッダファイル : calendar.h )
#include <stdio.h>
#include <iostream.h> // cout, endl に必要
#include <string.h> // string, getline に必要
#include <conio.h> // getch に必要
// Calendarクラスの宣言部分
class Calendar
{
protected:
int Floor(float); // ある数を超えない最大の整数
int GetWeekDay(int,int,int); // 曜日を求める
int GetDayCount(int,int); // ある月の日数を求める
bool IsLeapYear(int&); // うるう年かどうか
public:
Calendar(); // コンストラクタ
virtual ~Calendar(); // デストラクタ
bool GetDate(int*,int*); // 年と月の入力を求める
void SetCalendar(int,int); // カレンダーを作成する
} gc; // インスタンス gc を宣言
[2] コンストラクタ / デストラクタ
コンストラクタではタイトルを画面に表示します。
( ソースファイル : calendar.cpp )
#include "calendar.h"
// コンストラクタ
Calendar::Calendar()
{
cout << "********************************\n";
cout << " カレンダー表示プログラム\n"; // 見出し
cout << "********************************\n\n";
}
// デストラクタ
Calendar::~Calendar()
{
}
[3] ユーザー入力の受付
ユーザー入力を、年を変数year、月を変数monthに格納します。この際に"scanf"を使うと、異なるデータ型の入力や文字数オーバーと時にえらいことになりますので危険です。"getc(またはgetchar)"を使う手もありますが、ここでは、string型とgetline関数を使って入力を受け付けることにしました。ただし、string型はそのままでは加工が効かないので、"c_str"でCスタイルの文字列型(つまりnull文字で終わるcharの配列)に直して、さらに、"atoi"で数値に直します。"は数値と解釈できなければ値は0となります。受付が正常に終わればtrue、そうでなければfalseを返します。
// 表示年月の取得
bool Calendar::GetDate(int* year, int* month)
{
string s;
cout << "年を入力してください(1900-3000) > ";
getline(cin, s, '\n');
*year = atoi(s.c_str());
if (*year < 1900 || *year > 3000) {
cout << '\n';
return false;
}
cout << "月を入力してください(1-12) > " ;
getline(cin, s, '\n');
*month = atoi(s.c_str());
if (*month < 1 || *month > 12){
cout << '\n';
return false;
}
cout << '\n';
return true;
}
[4] 月の日数を求める
入力された月が何日間あるか調べます。大の月と小の月ですね。2月の時のみ、IsLeapYear関数に渡してうるう年かどうかを判定します。引数はmanth(月)とyear(年)ですが、yearはうるう年の判断のためです。終わったら日数(28〜31の整数)を返します。
// ある月の日数の取得
int Calendar::GetDayCount(int year, int month)
{
switch (month){
case 2:
if (IsLeapYear(year)) return 29;
else return 28;
case 4: return 30;
case 6: return 30;
case 9: return 30;
case 11:return 30;
default:return 31;
}
}
[5] うるう年かどうかを判断する
2月の時は、年の方を取り出し(引数year)て、うるう年か否かの判断をします。うるう年であればtrue、そうでなければfalseを返します。
・ 4で割り切れればうるう年。 ・ ただし、100で割り切れればうるう年でない。 ・ ただし、400で割り切れればうるう年。
// うるう年かどうか
bool Calendar::IsLeapYear(int& year)
{
if (!(year % 400)) return true;
if (!(year % 100)) return false;
if (!(year % 4)) return true;
return false;
}
[6] 月初が何曜日かを求める
カレンダーを表示する際には、スタート位置を指定する必要があります。そのため、月初が何曜日かを調べておくことが必須となります。求めるのは一日だけでいいのですが、GetWeekDayは年月日を入れれば(引数year、month、day)、どの日でも曜日を求められるようにしています。これを求める公式を、「Zellerの公式」というのだそうです。最初、この存在を知らなかったために、自前で長々とコーディングをしていました。といって、なぜこの式で曜日が求められるのかは聞かないでください。なにせ日曜プログラマですから。
Floor関数は標準ライブラリの中に既製のものがあるかもしれません。ちなみに、ここでは三項式を使っています。意味はありません。単に使ってみたかっただけです。もちろんif文に書き直せます。
// ある数を超えない最大の整数
int Calendar::Floor(float value)
{
return ((int)(value) > (value) ? (int)(value)-1 : (int)(value));
}
// 曜日の算出
int Calendar::GetWeekDay(int year, int month, int day)
{
// Zellerの公式に基づく
int n;
// 1月、2月の時は前の年の13月、14月として計算する
if (month < 3) {
month += 12;
year -=1;
}
// 戻り値(曜日) : 0(日) 1(月) 2(火) ... 6(土)
n = (year + Floor((float)(year/4)) - Floor((float)(year/100)) + Floor((float)(year/400))
+ Floor((float)(2.6*month+1.6)) + day);
return (n % 7);
}
[7] カレンダーの描画
SetCalendar関数は、前半で前述までの計算を行い、後半で描画を行っています。ここでは単色ですが、DOS画面でも色をつけたりすることができます。
インクリメント++weekdayは、weekdayに1を加えてから計算します。これが、weekday++になると、weekdayの値で計算してから1を足すことになります。
// カレンダーの設定
void Calendar::SetCalendar(int year, int month)
{
int days, weekday;
int i;
days = GetDayCount(year, month); // 月の日数
weekday = GetWeekDay(year, month, 1); // 月初の曜日
cout << " " << year << "年"<< month << "月\n";
cout << " 日 月 火 水 木 金 土" << endl;
cout << " --------------------" << endl;
// 1日まで空白で埋める
for (i = 0; i < weekday; i++)
printf(" ");
// 月の最終日まで表示
for (i = 1; i <= days; i++)
{
printf(" %2d", i);
// 日曜日の直前で改行
if (++weekday % 7 == 0)
printf("\n");
}
printf("\n\n");
}
[8] メイン関数
ユーザー入力を受け付け、カレンダーを表示します。Escキーを押すとプログラムは終了します。0x1BはEscのコード番号です。(基本文字コード表)
// main : アプリケーションのエントリポイント
int main(void)
{
// ループ処理
while (1)
{
// アプリケーションの開始
int year, month;
if (gc.GetDate(&year, &month))
gc.SetCalendar(year, month);
// アプリケーションの終了
cout << "[Esc]キーを押すと終了します。\n\n";
if (getch()==0x1B) return 0;
}
}
祭日を赤で表示したり、スケジュールを書き込めるようにしたり、いろいろと応用が利くのではないでしょうか。祭日は春分の日と秋分の日が不定期なのですが、ある程度は式で算出できるようです。興味のある方は調べてみるといいと思います。
|
||
www.sasaraan.net |
(c) morijoh |