Index

HOME > プログラムTOP > C++



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