C言語で万年カレンダー

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
さと

C言語で万年カレンダー

#1

投稿記事 by さと » 12年前

#include <stdio.h>
#include <ctype.h>
#define MOZISU 256
#define UNDERYEAR 2001
#define TOPYEAR 3000

//関数プロトタイプ宣言

int kensa( char *string ); // 文字列が正数の数字列であるかどうかを検査する
int seisuu( char *string ); // 文字列から整数を得る
int zellerscongruence( int year , int month ); //ツェラーの公式
int shukuzitu( int month , int calen);//祝日の計算



int main( void ) {
char str1[MOZISU],str2[MOZISU] ;
int year,month; //年、月
int feb , fday; //2月の日数,指定された月の日数
int comp; //ツェラーの公式変数を補正した変数(日曜日0~土曜日6)
int counter1,counter2; //カウンター
int calen=1; //日にち
char shuku; //祝日名

/*入力された文字を判別し、間違っている値が入力されたらもう一度きくプログラム*/

/*年*/
do {
printf( "年を入力:" );
scanf( "%255s", str1 ); //バッファオーバーラン対策
if ( !kensa( str1 ) ) { //正数で無いなら
printf( "数字を入力してください。\n" );
}
else { //正数なら
year = seisuu( str1 ); // 整数に変換
if ( !( year >= UNDERYEAR && year <= TOPYEAR ) ) {
printf( "2001~3000で入力してください。\n" );
}
}
} while ( !( year >= UNDERYEAR && year <= TOPYEAR ) ); //繰り返す

/*月*/
do {
printf( "月を入力:" );
scanf( "%255s", str2 ); //バッファオーバーラン対策
if ( !kensa( str2 ) ) { //正数で無いなら
printf( "数字を入力してください。\n" );
}
else { //正数なら
month = seisuu( str2 ); // 整数に変換
if ( month!=1 && month!=2 && month!=3 && month!=4 && month!=5 && month!=6 && month!=7 && month!=8 && month!=9 && month!=10 && month!=11 && month!=12 ) {
printf( "1~12で入力してください。\n" );
}
}
} while (month!=1 && month!=2 && month!=3 && month!=4 && month!=5 && month!=6 && month!=7 && month!=8 && month!=9 && month!=10 && month!=11 && month!=12 ); //繰り返す


/*閏年判別*/
if ( year %4 != 0) {
feb = 28;
printf("閏年ではない年 \n");
} else if ( year %100 != 0) {
feb = 29;
printf("閏年である年 \n");
} else if ( year%400 != 0) {
feb = 28;
printf("閏年でない年 \n");
} else {
feb = 29;
printf("閏年である年 \n");
}

if ( month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12 ){
fday = 31;
}
if ( month==4 || month==6 || month==9 || month==11 ){
fday = 30;
}
if ( month == 2){
fday = feb;
}
if ( month == 1 || month == 2) {
year--;
month+=12;
}
/*ツェラーの公式をcompへ代入*/
comp = zellerscongruence( year , month );


/*カレンダーを表示する*/
printf("\n");
printf("SUN MON TUE WED TUE FRI SAT\n");
for (counter1=0 ; counter1 < comp ; counter1++) {
printf(" ");
}
for (counter2=0 ; counter2 < fday ; counter2++) {
if ( calen < 10){
printf(" ");
}
printf("%d ", calen);
if ( (calen+comp)%7 == 0 ){
printf("\n\n");
shukuzitu(month , calen); //祝日を表示
}
calen++;
}

printf("\n");


return 0;
}


/* 文字列が正数の数字列であるかどうかを検査する */

int kensa( char *string ) {
int counter = 0; //カウンター
for ( ; *string != 0; string ++ ) { //文字列の終わりまで
if ( *string == '.' ) {
counter ++;
if ( counter > 1 ) { //少数の数が1を超えると
return 0;
}
}
else { //少数ではないなら
if ( isdigit( *string ) == 0 ) { // 数字でないならば
return 0;
}
}
}
return 1;
}


/* 文字列から整数を得る */

int seisuu( char *string ) {
int n = 0; //変換値
for ( ; ( *string != 0 ) && ( *string != '.' ); string ++ ) { //文字列の終わりまたは小数点まで
n *= 10; //変換値を1つ上の位に上げる
n += ( *string - '0' ); // 数字を数値に変換し、変換値に足す
}
return n; //変換値を返す
}

/*ツェラーの公式*/
int zellerscongruence( int year , int month ){
int a,b,p1,p2,p3; //ツェラーの公式に使用する計算用数変数
int zeller;
b = year%100;
a = ( year-b )/100;
p1 = 26 * ( month+1 ) / 10;
p2 = b/4;
p3 = a/4;
zeller = ( 1 + p1 + b + p2 + p3 - 2 * a ) % 7;
zeller = ( zeller + 6 ) % 7;

return(zeller);
}


/*祝日の計算*/

int shukuzitu(int month , int calen){

if(month==1 && calen==1 ){
printf("元日");//元日
}
if(month==2 && calen==11){
printf("建国記念日");// 建国記念日
}
if(month==4 && calen==29){
printf("昭和の日");// 昭和の日
}
if(month==5 && calen==3 ){
printf("憲法記念日"); // 憲法記念日
}
if(month==5 && calen==4 ){
printf("みどりの日"); // みどりの日
}
if(month==5 && calen==5 ){
printf("子供の日"); // 子供の日
}
if(month==11 && calen==3 ){
printf("文化の日"); // 文化の日
}
if(month==11 && calen==23){
printf("勤労感謝の日"); // 勤労感謝の日
}
if(month==12 && calen==23){
printf("天皇誕生日");// 天皇誕生日
}

}


製作途中です。一番下の祝日の計算が表示されません。どうすればよいでしょう
まだこれは固定された日付の祝日だけで、変動する祝日はもちろん表示したいです。
春分の日と秋分の日は国立天文台のホームページに乗っているので、それをコピーして表示したいです。
2031年以降は乗っていないので3月20日と9月20日に春分の日(仮)と秋分の日(仮)と表示します。
国民の休日や振り替え休日にももちろん対応したいです。

年と月を入力すればその年の月のカレンダーが出て、数字の下に祝日があれば祝日を表示します。

以上のようなプログラムにするにはどうすればよいでしょうか。

box
記事: 2002
登録日時: 15年前

Re: C言語で万年カレンダー

#2

投稿記事 by box » 12年前

昔、こんなコードを書いたことがあります。
参考になるかどうかはわかりません。

コード:

/******************************************************************************/
/* カレンダー出力プログラム                                                   */
/******************************************************************************/

/* <使い方>
 * cal [-h | -?] [-n] [-s] [year [month]] <Enter>
 *
 *   year :年(1851~2150)
 *   month:月(1~12)
 *
 *   年・月の両方を省略すると、現在の月のカレンダーを出力
 *   年だけを指定して月を省略すると、その年のカレンダーを出力
 *   年・月の両方を指定すると、その年・月のカレンダーを出力
 *   '-n'を指定すると、日曜・祝日のマークを付けない(デフォルト)
 *   '-s'を指定すると、現行の祝日法に従った日曜・祝日のマークを付ける
 *   '-h'または'-?'を指定すると、使い方を出力
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

/* ---- 記号定数 ---- */

#define MONTHS_PER_YEAR     (12)                    /* 1年の月数 */
#define WEEKS_PER_MONTH     (6)                     /* 1ヶ月の最大週数 */
#define DAYS_PER_WEEK       (7)                     /* 1週間の日数 */
#define YEAR_LOWER_LIMIT    (1851)                  /* 年の下限 */
#define YEAR_UPPER_LIMIT    (2150)                  /* 年の上限 */
#define MONTH_LOWER_LIMIT   (1)                     /* 月の下限 */
#define MONTH_UPPER_LIMIT   (MONTHS_PER_YEAR)       /* 月の上限 */
#define LENGTH              (40)                    /* 年・月や週の文字列長 */

/* ---- 構造体定義 ---- */

/* 国民の祝日・国民の休日の定義用 */
typedef struct {
    int month;                                      /* 月 */
    int day_or_week;                                /* 日または週 */
    int week;                                       /* 曜日(0:日曜~6:土曜) */
                                    /* 日にちが決まっている祝日の場合は負の数 */
} HOLIDAYS;

/* 出力形式の定義用 */
typedef struct {
    char *week;                                     /* 曜日の名前 */
    char *date;                                     /* 日にち */
    int space;                                      /* 無効な日にち用の空白数 */
    int horizontal_months;                          /* 横方向に出力する月数 */
    int horizontal_space;                           /* 月どうしの横方向の間隔 */
    int vertical_space;                             /* 月どうしの行間 */
} FORMAT;

/* ---- 外部変数など ---- */

HOLIDAYS national_holidays[] = {                    /* 国民の祝日 */
    { 1,  1, -1},                                   /* 1月1日:元日 */
    { 1,  2,  1},                                   /* 1月第2月曜日:成人の日 */
    { 2, 11, -1},                                   /* 2月11日:建国記念の日 */
    { 3,  0, -1},                                   /* 春分の日 */
    { 4, 29, -1},                                   /* 4月29日:昭和の日 */
    { 5,  3, -1},                                   /* 5月3日:憲法記念日 */
    { 5,  4, -1},                                   /* 5月4日:みどりの日 */
    { 5,  5, -1},                                   /* 5月5日:こどもの日 */
    { 7,  3,  1},                                   /* 7月第3月曜日:海の日 */
    { 9,  3,  1},                                   /* 9月第3月曜日:敬老の日 */
    { 9,  0, -1},                                   /* 秋分の日 */
    {10,  2,  1},                                   /* 10月第2月曜日:体育の日 */
    {11,  3, -1},                                   /* 11月3日:文化の日 */
    {11, 23, -1},                                   /* 11月23日:勤労感謝の日 */
    {12, 23, -1},                                   /* 12月23日:天皇誕生日 */
    {99,  0, -1},                                   /* 番兵 */
};
FORMAT format[] = {                                 /* 出力形式 */
    {"%2s " , "%2d " , 3, 3, 6, 2},                 /* ノーマル */
    {" %2s ", "<%2d>", 4, 2, 8, 2},                 /* スペシャル1 */
    {" %2s ", " %2d ", 4, 2, 8, 2},                 /* スペシャル2 */
};
int first_sunday[MONTHS_PER_YEAR+1];                /* 毎月第1日曜日の日にち */
int year_is_set;                                    /* 年のセット方法 */
int month_is_set;                                   /* 月のセット方法 */

enum {                                              /* 出力パターンと対応 */
    NORMAL,                                         /* 日曜・祝日のマークなし */
    SPECIAL,                                        /* 日曜・祝日のマークあり */
};
enum {                                              /* 年・月のセット方法 */
    BY_AUTOMATIC,                                   /* 自動 */
    BY_MANUAL,                                      /* 手動(引数で与える) */
};
enum {                                              /* エラーメッセージと対応 */
    INVALID_OPT,                                    /* 不正なオプション */
    INVALID_NUM,                                    /* 不正な数値 */
    INVALID_Y_OR_M,                                 /* 不正な年または月 */
};
typedef enum {
    FALSE,                                          /* 偽 */
    TRUE,                                           /* 真 */
} Boolean;

/* ---- 関数プロトタイプ宣言 ---- */

void getopt(int argc, char **argv, int *pat, int *y, int *m);
char *set_week_name(int pat, char *w);
void get_vernal_and_autumnal(int y);
void get_date_of_first_sunday(int y);
void print_calendar(int pat, int y, int m, char *w);
void print_one_month(int pat, int y, int m, char *w);
void print_one_year(int pat, int y, char *w);
void print_year_and_month_name(int y, int m, int len);
void print_week_name(char *w);
void print_date(int pat, int y, int m, int d, int u);
Boolean is_holiday(int m, int d, int u);
void print_space_between_months(int pat, int m);
void print_space(int n);
void print_new_line(int n);
int day_of_week(int y, int m, int d);
int leap(int y);
void print_error_msg(int n);
void usage(char *pgm);
char *remove_path_and_extension(char *s);

/*----------------------------------------------------------------------------*/
/* メイン                                                                     */
/*                                                                            */
/*     引数                                                                   */
/*         argc     コマンド行の引数の個数                                    */
/*         argv     引数の文字列へのポインターへのポインター                  */
/*     戻り値                                                                 */
/*         EXIT_SUCCESS     正常終了                                          */
/*----------------------------------------------------------------------------*/
int main(int argc, char **argv)
{
    int pattern;                                    /* 出力パターン */
    int year, month;                                /* 年・月 */
    char week[LENGTH+1];                            /* 曜日の名前 */
    
    getopt(argc, argv, &pattern, &year, &month);    /* 引数の読み込み */
    set_week_name(pattern, week);                   /* 曜日の名前のセット */
    get_vernal_and_autumnal(year);                  /* 春分の日・秋分の日 */
    get_date_of_first_sunday(year);                 /* 毎月第1日曜日の日にち */
    print_calendar(pattern, year, month, week);     /* カレンダーの出力 */
    return EXIT_SUCCESS;
}

/*----------------------------------------------------------------------------*/
/* 引数の読み込み                                                             */
/*                                                                            */
/*     引数                                                                   */
/*         argc     コマンド行の引数の個数                                    */
/*         argv     引数の文字列へのポインターへのポインター                  */
/*         pat      出力パターンの値へのポインター(呼び出し元へ返却)        */
/*         y        年の値へのポインター(呼び出し元へ返却)                  */
/*         m        月の値へのポインター(呼び出し元へ返却)                  */
/*     戻り値                                                                 */
/*         なし                                                               */
/*----------------------------------------------------------------------------*/
void getopt(int argc, char **argv, int *pat, int *y, int *m)
{
    char *pgm = *argv;                              /* 実行プログラム名 */
    struct tm *tm;                                  /* ローカル時間保存用 */
    time_t t = time(NULL);                          /* 現在のカレンダー時間 */
    int opt;                                        /* 実行時のオプション */
    int wk;                                         /* 年・月の値の一時保管用 */
    
    /* 出力パターン、年・月の初期設定 */
    *pat = NORMAL;                                  /* 日曜・祝日のマークなし */
    tm = localtime(&t);                             /* 年・月は現在の値 */
    *y = tm->tm_year + 1900;
    *m = tm->tm_mon  + 1;
    year_is_set = month_is_set = BY_AUTOMATIC;      /* 年・月とも自動セット */
    
    /* 実行時引数の処理 */
    while (--argc) {                                /* 引数の数だけループ */
        opt = **(++argv);                           /* 引数の先頭文字 */
        if (opt == '-') {                           /* '-'で始まっていると */
            opt = *++(*argv);                       /* その次がオプション */
            switch (opt) {
            case 'n':
                *pat = NORMAL;                      /* 日曜・祝日のマークなし */
                break;
            case 's':
                *pat = SPECIAL;                     /* 日曜・祝日のマークあり */
                break;
            case 'h': case '?':
                usage(pgm);                         /* 使い方を出力 */
                break;
            default:                                /* エラー */
                print_error_msg(isdigit(opt) ? INVALID_NUM : INVALID_OPT);
                usage(pgm);
            }
        }
        else {                                      /* '-'以外で始まっている */
            if (!isdigit(opt)) {                    /* 数字でなければ */
                print_error_msg(INVALID_OPT);       /* 不正なオプション */
                usage(pgm);
            }
            
            wk = atoi(*argv);                       /* 数値を一時的に保管 */
            
            /* 年・月とみなせない範囲ならば */
            if (((wk < YEAR_LOWER_LIMIT ) || (YEAR_UPPER_LIMIT  < wk)) &&
                ((wk < MONTH_LOWER_LIMIT) || (MONTH_UPPER_LIMIT < wk))) {
                print_error_msg(INVALID_Y_OR_M);    /* 不正な年または月 */
                usage(pgm);
            }
            /* 月とみなせる範囲ならば */
            if ((MONTH_LOWER_LIMIT <= wk) && (wk <= MONTH_UPPER_LIMIT)) {
                if (year_is_set != BY_MANUAL) {     /* 年が手動セット済でない */
                    print_error_msg(INVALID_Y_OR_M);    /* 不正な年または月 */
                    usage(pgm);
                }
                else {                              /* 年が手動セット済ならば */
                    *m = wk;                        /* 月とする */
                    month_is_set = BY_MANUAL;       /* 月を手動セット済にする */
                }
            }
            /* 年とみなせる範囲ならば */
            else {
                *y = wk;                            /* 年とする */
                year_is_set = BY_MANUAL;            /* 年を手動セット済にする */
            }
        }
    }
}

/*----------------------------------------------------------------------------*/
/* 曜日の名前のセット                                                         */
/*                                                                            */
/*     引数                                                                   */
/*         pat      出力パターン                                              */
/*         w        出力パターンに応じた曜日の名前を表わす文字列への          */
/*                  ポインター(呼び出し元へ返却)                            */
/*     戻り値                                                                 */
/*         出力パターンに応じた曜日の名前を表わす文字列へのポインター         */
/*         (今回、呼び出し元では戻り値を特に使ってはいない)                 */
/*----------------------------------------------------------------------------*/
char *set_week_name(int pat, char *w)
{
    static char *wname[] = {                        /* 曜日の名前 */
        "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"
    };
    int i, j;
    
    for (i = j = 0; i < DAYS_PER_WEEK; i++, j += format[pat].space) {
        sprintf(&w[j], format[pat].week, wname[i]);
    }
    return w;
}

/*----------------------------------------------------------------------------*/
/* 春分の日・秋分の日の計算                                                   */
/*                                                                            */
/*     引数                                                                   */
/*         y        年                                                        */
/*     戻り値                                                                 */
/*         なし                                                               */
/*----------------------------------------------------------------------------*/
void get_vernal_and_autumnal(int y)
{
    int vernal;                                     /* 春分の日の日にち */
    int autumnal;                                   /* 秋分の日の日にち */
    int i;
    
    /* 以下の計算式の出典は、
     * 株式会社恒星社厚生閣発行  暦計算研究会編「新こよみ便利帳」
     */
    if (y >= 2100) {
        vernal   = 21.8510 + 0.242194 * ((double) y - 1980.0) -
            (int) (((double) y - 1980.0) / 4.0);
        autumnal = 24.2488 + 0.242194 * ((double) y - 1980.0) -
            (int) (((double) y - 1980.0) / 4.0);
    }
    else if (y >= 1980) {
        vernal   = 20.8431 + 0.242194 * ((double) y - 1980.0) -
            (int) (((double) y - 1980.0) / 4.0);
        autumnal = 23.2488 + 0.242194 * ((double) y - 1980.0) -
            (int) (((double) y - 1980.0) / 4.0);
    }
    else if (y >= 1900) {
        vernal   = 20.8357 + 0.242194 * ((double) y - 1980.0) -
            (int) (((double) y - 1983.0) / 4.0);
        autumnal = 23.2588 + 0.242194 * ((double) y - 1980.0) -
            (int) (((double) y - 1983.0) / 4.0);
    }
    else {
        vernal   = 19.8277 + 0.242194 * ((double) y - 1980.0) -
            (int) (((double) y - 1983.0) / 4.0);
        autumnal = 22.2588 + 0.242194 * ((double) y - 1980.0) -
            (int) (((double) y - 1983.0) / 4.0);
    }
    
    /* 祝日テーブルにセット */
    for (i = 0; national_holidays[i].month <= MONTHS_PER_YEAR; i++) {
        if (national_holidays[i].day_or_week == 0) {
            if (national_holidays[i].month == 3) {
                national_holidays[i].day_or_week = vernal;      /* 春分の日 */
            }
            if (national_holidays[i].month == 9) {
                national_holidays[i].day_or_week = autumnal;    /* 秋分の日 */
            }
        }
    }
}

/*----------------------------------------------------------------------------*/
/* 毎月第1日曜日の日にちを求める                                              */
/*                                                                            */
/*     引数                                                                   */
/*         y        年                                                        */
/*     戻り値                                                                 */
/*         なし                                                               */
/*----------------------------------------------------------------------------*/
void get_date_of_first_sunday(int y)
{
    int m;                                          /* 月 */
    
    /* 毎月の初日の曜日をもとに、第1日曜日が何日であるかを求める
     * 0以下の値になってもかまわない(出力しないだけのこと)
     */
    for (m = 1; m <= MONTHS_PER_YEAR; m++) {
        first_sunday[m] = 1 - day_of_week(y, m, 1);
    }
}

/*----------------------------------------------------------------------------*/
/* カレンダーの出力                                                           */
/*                                                                            */
/*     引数                                                                   */
/*         pat      出力パターン                                              */
/*         y        年                                                        */
/*         m        月                                                        */
/*         w        出力パターンに応じた曜日の名前を表わす文字列への          */
/*                  ポインター                                                */
/*     戻り値                                                                 */
/*         なし                                                               */
/*----------------------------------------------------------------------------*/
void print_calendar(int pat, int y, int m, char *w)
{
    /* 年・月のセット方法が同じとき */
    if (year_is_set == month_is_set) {
        print_one_month(pat, y, m, w);              /* 1ヶ月分を出力 */
    }
    /* 年・月のセット方法が異なるとき(実質的には年だけを手でセットしたとき) */
    else {
        print_one_year(pat, y, w);                  /* 1年分を出力 */
    }
}

/*----------------------------------------------------------------------------*/
/* 1ヶ月分のカレンダーの出力                                                  */
/*                                                                            */
/*     引数                                                                   */
/*         pat      出力パターン                                              */
/*         y        年                                                        */
/*         m        月                                                        */
/*         w        出力パターンに応じた曜日の名前を表わす文字列への          */
/*                  ポインター                                                */
/*     戻り値                                                                 */
/*         なし                                                               */
/*----------------------------------------------------------------------------*/
void print_one_month(int pat, int y, int m, char *w)
{
    int len = strlen(w);                            /* 曜日用文字列の長さ */
    int d;                                          /* 日にち */
    int u;                                          /* 日曜日の日にち */
    int i;
    
    print_year_and_month_name(y, m, len);           /* 年・月名の出力 */
    print_new_line(1);
    print_week_name(w);                             /* 曜日名の出力 */
    print_new_line(1);
    
    /* 月によっては6週にまたがることがある */
    for (i = 0; i < WEEKS_PER_MONTH; i++) {
        /* 第1日曜日から見かけ上の末日まで */
        u = first_sunday[m] + i * DAYS_PER_WEEK;
        for (d = u; d < u + DAYS_PER_WEEK; d++) {
            print_date(pat, y, m, d, u);            /* 日にちの出力 */
        }
        print_new_line(1);                          /* 週ごとに改行 */
    }
}

/*----------------------------------------------------------------------------*/
/* 1年分のカレンダーの出力                                                    */
/*                                                                            */
/*     引数                                                                   */
/*         pat      出力パターン                                              */
/*         y        年                                                        */
/*         w        出力パターンに応じた曜日の名前を表わす文字列への          */
/*                  ポインター                                                */
/*     戻り値                                                                 */
/*         なし                                                               */
/*----------------------------------------------------------------------------*/
void print_one_year(int pat, int y, char *w)
{
    int m;                                          /* 月 */
    int d;                                          /* 日にち */
    int u;                                          /* 日曜日の日にち */
    int h = format[pat].horizontal_months;          /* 横方向に出力する月数 */
    int len = strlen(w);                            /* 曜日用文字列の長さ */
    int i, j;
    
    /* 横方向に所定の月数分出力することを繰り返す */
    for (i = 1; i <= MONTHS_PER_YEAR; i += h) {
        for (m = i; m < i + h; m++) {
            print_year_and_month_name(y, m, len);   /* 年・月名の出力 */
            print_space_between_months(pat, m);     /* 月どうしの間隔・改行 */
        }
        for (m = i; m < i + h; m++) {
            print_week_name(w);                     /* 曜日名の出力 */
            print_space_between_months(pat, m);     /* 月どうしの間隔・改行 */
        }
        
        /* 月によっては6週にまたがることがある */
        for (j = 0; j < WEEKS_PER_MONTH; j++) {
            for (m = i; m < i + h; m++) {
                /* 第1日曜日から見かけ上の末日まで */
                u = first_sunday[m] + j * DAYS_PER_WEEK;
                for (d = u; d < u + DAYS_PER_WEEK; d++) {
                    print_date(pat, y, m, d, u);    /* 日にちの出力 */
                }
                print_space_between_months(pat, m); /* 月どうしの間隔・改行 */
            }
        }
        print_new_line(format[pat].vertical_space);	/* 月どうしの行間 */
    }
}

/*----------------------------------------------------------------------------*/
/* 年・月名の出力                                                             */
/*                                                                            */
/*     引数                                                                   */
/*         y        年                                                        */
/*         m        月                                                        */
/*         len      出力パターンに応じた曜日の名前を表わす文字列の長さ        */
/*     戻り値                                                                 */
/*         なし                                                               */
/*----------------------------------------------------------------------------*/
void print_year_and_month_name(int y, int m, int len)
{
    char str[LENGTH+1];                             /* 年・月を格納 */
    static char *mname[MONTHS_PER_YEAR+1] = {       /* 月の名前 */
        "",
        "January", "February", "March",     "April",   "May",      "June",
        "July",    "August",   "September", "October", "November", "December",
    };
    
    sprintf(str, "%d %s", y, mname[m]);             /* 年・月名 */
    printf("%s", str);
    
    /* 横方向に複数の月を出力する場合に備えて、年・月名の後ろに空白を空ける */
    print_space(len - strlen(str));
}

/*----------------------------------------------------------------------------*/
/* 曜日名の出力                                                               */
/*                                                                            */
/*     引数                                                                   */
/*         w        出力パターンに応じた曜日の名前を表わす文字列への          */
/*                  ポインター                                                */
/*     戻り値                                                                 */
/*         なし                                                               */
/*----------------------------------------------------------------------------*/
void print_week_name(char *w)
{
    printf("%s", w);                                /* 曜日名 */
}

/*----------------------------------------------------------------------------*/
/* 日にちの出力                                                               */
/*                                                                            */
/*     引数                                                                   */
/*         pat      出力パターン                                              */
/*         y        年                                                        */
/*         m        月                                                        */
/*         d        日                                                        */
/*         u        y年m月d日が属する週の日曜日の日にち                       */
/*     戻り値                                                                 */
/*         なし                                                               */
/*----------------------------------------------------------------------------*/
void print_date(int pat, int y, int m, int d, int u)
{
    static int daytab[][MONTHS_PER_YEAR+1] = {                  /* 毎月の日数 */
        {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},    /* 平年 */
        {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},    /* 閏年 */
    };
    int i;
    
    if ((1 <= d) && (d <= daytab[leap(y)][m])) {            /* 有効な日にち */
        i = ((pat == NORMAL) || is_holiday(m, d, u)) ? 0 : 1;
        printf(format[pat+i].date, d);                      /* パターンに従う */
    }
    else {                                                  /* 無効な日にち */
        print_space(format[pat].space);                     /* 空白を出力 */
    }
}

/*----------------------------------------------------------------------------*/
/* 日曜・祝日かどうかのチェック                                               */
/*                                                                            */
/*     引数                                                                   */
/*         m        月                                                        */
/*         d        日                                                        */
/*         u        その年のm月d日が属する週の日曜日の日にち                  */
/*     戻り値                                                                 */
/*         TRUE     日曜・祝日である                                          */
/*         FALSE    日曜・祝日ではない                                        */
/*----------------------------------------------------------------------------*/
Boolean is_holiday(int m, int d, int u)
{
    int d_w;                                        /* 日または週 */
    int w;                                          /* 曜日(0:日曜~6:土曜) */
                                    /* 日にちが決まっている祝日の場合は負の数 */
    int i;
    
    /* 週の初日(日曜日) */
    if (d == u) {
        return TRUE;
    }
    
    for (i = 0; national_holidays[i].month <= m; i++) {
        /* その月に祝日があれば */
        if (m == national_holidays[i].month) {
            d_w = national_holidays[i].day_or_week;
            w   = national_holidays[i].week;
            
            /* 日にちが決まっている祝日 */
            if ((w < 0) && (d == d_w)) {
                return TRUE;
            }
            /* 祝日が日曜日と重なる場合、翌日は振替休日 */
            if ((w < 0) && (u == d_w) && (d == u + 1)) {        /* +1は月曜日 */
                return TRUE;
            }
            /* 前項に加え、その日より後で祝日でない日が振替休日となる
             * 現行の祝日法では、5月6日が月曜日~水曜日の場合が相当する
             */
            if ((m == 5) && (d == 6) && (3 <= u) && (u <= 5)) {
                return TRUE;
            }
            /* 第n○曜日(成人の日など) */
            if ((w >= 0) && (d == u + w) &&
                ((d_w - 1) * DAYS_PER_WEEK < d) && (d <= d_w * DAYS_PER_WEEK)) {
                return TRUE;
            }
            /* 前日と翌日がともに祝日である日は休日
             * 現行の祝日法では、9月第3月曜日の敬老の日が21日で、
             * 秋分の日が23日である場合に限り、それらの間にはさまれた
             * 22日(火)が休日となる
             * (法改正に伴ってロジックを修正する可能性が生じる)
             */
            if ((m == 9) && (w < 0) && (d == d_w - 1) &&    /* 秋分の日の前日 */
                (d == u + 2) &&                             /* +2は火曜日 */
                (d - 1 == DAYS_PER_WEEK * 3)) {             /* 敬老の日が21日 */
                return TRUE;
            }
        }
    }
    return FALSE;
}

/*----------------------------------------------------------------------------*/
/* 月どうしの間隔・改行                                                       */
/*                                                                            */
/*     引数                                                                   */
/*         pat      出力パターン                                              */
/*         m        月                                                        */
/*     戻り値                                                                 */
/*         なし                                                               */
/*----------------------------------------------------------------------------*/
void print_space_between_months(int pat, int m)
{
    if (m % format[pat].horizontal_months != 0) {
        print_space(format[pat].horizontal_space);          /* 月どうしの間隔 */
    }
    else {
        print_new_line(1);                      /* 所定の月数分出力したら改行 */
    }
}

/*----------------------------------------------------------------------------*/
/* 空白の出力                                                                 */
/*                                                                            */
/*     引数                                                                   */
/*         n        空白の数                                                  */
/*     戻り値                                                                 */
/*         なし                                                               */
/*----------------------------------------------------------------------------*/
void print_space(int n)
{
    while (n--) {
        putchar(' ');
    }
}

/*----------------------------------------------------------------------------*/
/* 改行                                                                       */
/*                                                                            */
/*     引数                                                                   */
/*         n        改行の数                                                  */
/*     戻り値                                                                 */
/*         なし                                                               */
/*----------------------------------------------------------------------------*/
void print_new_line(int n)
{
    while (n--) {
        putchar('\n');
    }
}

/*----------------------------------------------------------------------------*/
/* 曜日の計算(原典は坂本智彦氏によるコード)                                 */
/*                                                                            */
/*     引数                                                                   */
/*         y        年                                                        */
/*         m        月                                                        */
/*         d        日                                                        */
/*     戻り値                                                                 */
/*         0        日曜日(以下、1~6が月曜日~土曜日)                      */
/*----------------------------------------------------------------------------*/
int day_of_week(int y, int m, int d)
{
    static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
    
    y -= m < 3;                                     /* 1~2月は前年とみなす */
    return (y + y / 4 - y / 100 + y / 400 + t[m-1] + d) % 7;
}

/*----------------------------------------------------------------------------*/
/* 閏年かどうかのチェック                                                     */
/*                                                                            */
/*     引数                                                                   */
/*         y        年                                                        */
/*     戻り値                                                                 */
/*         0        平年である                                                */
/*         1        閏年である                                                */
/*----------------------------------------------------------------------------*/
int leap(int y)
{
    /* 「4で割り切れて100で割り切れない」または「400で割り切れる」年が閏年 */
    return ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0);
}

/*----------------------------------------------------------------------------*/
/* エラーメッセージの出力                                                     */
/*                                                                            */
/*     引数                                                                   */
/*         n        エラー番号                                                */
/*     戻り値                                                                 */
/*         なし                                                               */
/*----------------------------------------------------------------------------*/
void print_error_msg(int n)
{
    static char *error_msg[] = {                    /* エラーメッセージ */
        "オプションの指定が正しくありません。\n\n",
        "年・月に負の数は指定できません。\n\n",
        "年または月の値が正しくありません。\n\n",
    };
    
    fprintf(stderr, "E%03d:%s", n, error_msg[n]);
}

/*----------------------------------------------------------------------------*/
/* プログラムの使い方の出力                                                   */
/*                                                                            */
/*     引数                                                                   */
/*         pgm      プログラム名のフルパスを表わす文字列へのポインター        */
/*     戻り値                                                                 */
/*         なし                                                               */
/*----------------------------------------------------------------------------*/
void usage(char *pgm)
{
    char *s = remove_path_and_extension(pgm);       /* パスと拡張子の除去 */
    
    fprintf(stderr, "<使い方>\n");
    fprintf(stderr, "%s [-h | -?] [-n] [-s] [year [month]] <Enter>\n\n", s);
    fprintf(stderr, "  year :年(%d~%d)\n",
        YEAR_LOWER_LIMIT, YEAR_UPPER_LIMIT);
    fprintf(stderr, "  month:月(%d~%d)\n\n",
        MONTH_LOWER_LIMIT, MONTH_UPPER_LIMIT);
    fprintf(stderr, "  年・月の両方を省略すると、現在の月のカレンダーを出力\n");
    fprintf(stderr, "  年だけを指定して月を省略すると、その年のカレンダーを"
        "出力\n");
    fprintf(stderr, "  年・月の両方を指定すると、その年・月のカレンダーを"
        "出力\n");
    fprintf(stderr, "  '-n'を指定すると、日曜・祝日のマークを付けない"
        "(デフォルト)\n");
    fprintf(stderr, "  '-s'を指定すると、現行の祝日法に従った日曜・祝日の"
        "マークを付ける\n");
    fprintf(stderr, "  '-h'または'-?'を指定すると、使い方を出力\n");
    exit(EXIT_FAILURE);
}

/*----------------------------------------------------------------------------*/
/* パスと拡張子の除去                                                         */
/*                                                                            */
/*     引数                                                                   */
/*         s        プログラム名のフルパスを表わす文字列へのポインター        */
/*                  (フルパスを受け取り、必要に応じてディレクトリーと        */
/*                  拡張子の部分を取り除いた結果を呼び出し元へ返す)          */
/*     戻り値                                                                 */
/*         プログラム名を表わす文字列へのポインター                           */
/*----------------------------------------------------------------------------*/
char *remove_path_and_extension(char *s)
{
    char *str;                          /* 最も右にある'\'以降の文字列を指す */
    int i;
    
    /* 実行プログラム名のフルパスから、ディレクトリーと拡張子の部分を取り除く
     * MS-DOSの流れを受け継ぐOSを想定し、ディレクトリー区切り文字を'\'とする
     */
    str = strrchr(s, '\\');                 /* 最も右にある'\'以降を求める */
    if (str != NULL) {                      /* 求まったら */
        str++;                              /* 1文字分進める */
        i = strcspn(str, ".");              /* 拡張子区切り文字までの文字数 */
        strncpy(s, str, i);                 /* その文字数分だけコピー */
        s[i] = '\0';                        /* 文字列の終端 */
        while (i--) {                       /* UNIXっぽくするため */
            s[i] = (char) tolower(s[i]);    /* 小文字に変換 */
        }
    }
    
    /* ディレクトリー区切り文字がなければ、そのまま戻る */
    return s;
}
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

さと

Re: C言語で万年カレンダー

#3

投稿記事 by さと » 12年前

ありがとうございます!ですが、私の勉強不足で、何が何だかわかりません・・・・
頑張って勉強して1年後にはこのようなプログラムが書けるようになりたいです。

box
記事: 2002
登録日時: 15年前

Re: C言語で万年カレンダー

#4

投稿記事 by box » 12年前

700行以上のコードを1時間かそこらで読み解けるとは、だれも思っていません。
すぐにあきらめるのではなく、がんばって読んでみてはどうでしょうか。
むずかしいアルゴリズムやデータ構造は、全く使っていません。
まずは、コードの先頭部分に書いてある使い方に従って実行してみて、
「ふむふむ。こういうパラメータを入れるとこういう出力をするのか」というところを
理解してみてはどうでしょう。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

さと

Re: C言語で万年カレンダー

#5

投稿記事 by さと » 12年前

質問よろしいでしょうか。
このプログラムは詳細設計書などを書きそれをもとに作成されたのでしょうか。
それともいきなり打ち込んで作成されたのでしょうか。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: C言語で万年カレンダー

#6

投稿記事 by softya(ソフト屋) » 12年前

勝手に返事しちゃいますが、きっちりとした会社でもない限りは詳細設計書など余り書きません。訓練には書いたほうが良いでしょうが。

私とかだと最終的に一万行を超えるようなものでも、雑な設計メモからスタートします。
雑把な仕様メモ → モジュールの雑な分割メモ → 必要な構造体などの作成 → ファイルごとの基本的な関数を準備
と言った感じです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

さと

Re: C言語で万年カレンダー

#7

投稿記事 by さと » 12年前

#include <stdio.h>
#include <ctype.h>
#define MOZISU 256
#define ALLDAY 365
#define ALLWEEK 7
#define ALLYEAR 1000
#define UNDERYEAR 2001
#define TOPYEAR 3000

//関数プロトタイプ宣言

int kensa( char *string ); // 文字列が正数の数字列であるかどうかを検査する
int seisuu( char *string ); // 文字列から整数を得る
int zellerscongruence( int year , int month ); //ツェラーの公式
int shukuzitu( int month , int counter2 , int comp , int year );//祝日の計算

int true; //祝日があるなら1

int main( void ) {
char str1[MOZISU],str2[MOZISU] ;
int year,month; //年、月
int feb , fday; //2月の日数,指定された月の日数
int comp; //ツェラーの公式変数を補正した変数(日曜日0~土曜日6)
int counter1,counter2,counter3; //カウンター
int calen=1; //日にち
char shuku; //祝日名
int youbi; //曜日

/*入力された文字を判別し、間違っている値が入力されたらもう一度きくプログラム*/

/*年*/
do {
printf( "年を入力:" );
scanf( "%255s", str1 ); //バッファオーバーラン対策
if ( !kensa( str1 ) ) { //正数で無いなら
printf( "数字を入力してください。\n" );
}
else { //正数なら
year = seisuu( str1 ); // 整数に変換
if ( !( year >= UNDERYEAR && year <= TOPYEAR ) ) {
printf( "2001~3000で入力してください。\n" );
}
}
} while ( !( year >= UNDERYEAR && year <= TOPYEAR ) ); //繰り返す

/*月*/
do {
printf( "月を入力:" );
scanf( "%255s", str2 ); //バッファオーバーラン対策
if ( !kensa( str2 ) ) { //正数で無いなら
printf( "数字を入力してください。\n" );
}
else { //正数なら
month = seisuu( str2 ); // 整数に変換
if ( month!=1 && month!=2 && month!=3 && month!=4 && month!=5 && month!=6 && month!=7 && month!=8 && month!=9 && month!=10 && month!=11 && month!=12 ) {
printf( "1~12で入力してください。\n" );
}
}
} while (month!=1 && month!=2 && month!=3 && month!=4 && month!=5 && month!=6 && month!=7 && month!=8 && month!=9 && month!=10 && month!=11 && month!=12 ); //繰り返す


/*閏年判別*/
if ( year %4 != 0) {
feb = 28;
printf("閏年ではない年 \n");
} else if ( year %100 != 0) {
feb = 29;
printf("閏年である年 \n");
} else if ( year%400 != 0) {
feb = 28;
printf("閏年でない年 \n");
} else {
feb = 29;
printf("閏年である年 \n");
}

if ( month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12 ){
fday = 31;
}
if ( month==4 || month==6 || month==9 || month==11 ){
fday = 30;
}
if ( month == 2){
fday = feb;
}
if ( month == 1 || month == 2) {
year--;
month+=12;
}
/*ツェラーの公式をcompへ代入*/
comp = zellerscongruence( year , month ); //月の初めの曜日


/*カレンダーを表示する*/

youbi=comp;
printf("\n");
printf("SUN MON TUE WED TUE FRI SAT\n");
for (counter1=0 ; counter1 < comp ; counter1++) {//最初の曜日までスペースを表示
printf(" ");
}
for (counter2=0 ; counter2 < fday ; counter2++ , youbi++){

if ( calen < 10){ //列を合わせる
printf(" ");
}

printf("%d ", calen);
shukuzitu( month , counter2 , youbi , year);
if ( (calen+comp)%7 == 0 ){
printf("\n");
printf("\n");
}

calen++;


}

printf("\n");
return 0;
}


/* 文字列が正数の数字列であるかどうかを検査する */

int kensa( char *string ) {
int counter = 0; //カウンター
for ( ; *string != 0; string ++ ) { //文字列の終わりまで
if ( *string == '.' ) {
counter ++;
if ( counter > 1 ) { //少数の数が1を超えると
return 0;
}
}
else { //少数ではないなら
if ( isdigit( *string ) == 0 ) { // 数字でないならば
return 0;
}
}
}
return 1;
}


/* 文字列から整数を得る */

int seisuu( char *string ) {
int n = 0; //変換値
for ( ; ( *string != 0 ) && ( *string != '.' ); string ++ ) { //文字列の終わりまたは小数点まで
n *= 10; //変換値を1つ上の位に上げる
n += ( *string - '0' ); // 数字を数値に変換し、変換値に足す
}
return n; //変換値を返す
}

/*ツェラーの公式*/
int zellerscongruence( int year , int month ){
int a,b,p1,p2,p3; //ツェラーの公式に使用する計算用数変数
int zeller;
b = year%100;
a = ( year-b )/100;
p1 = 26 * ( month+1 ) / 10;
p2 = b/4;
p3 = a/4;
zeller = ( 1 + p1 + b + p2 + p3 - 2 * a ) % 7;
zeller = ( zeller + 6 ) % 7;

return(zeller);
}


/*祝日の計算*/

int shukuzitu(int month , int counter2 , int youbi , int year){

if(month==13 && counter2==1-1 ){
printf("元日");//元日
}
if(month==14 && counter2==11-1){
printf("建国記念日");// 建国記念日
}
if(month==4 && counter2==29-1){
printf("昭和の日");// 昭和の日
}
if(month==5 && counter2==3-1 ){
printf("憲法記念日"); // 憲法記念日
}
if(month==5 && counter2==4-1 ){
printf("みどりの日"); // みどりの日
}
if(month==5 && counter2==5-1 ){
printf("子供の日"); // 子供の日
}
if(month==11 && counter2==3-1 ){
printf("文化の日"); // 文化の日
}
if(month==11 && counter2==23-1){
printf("勤労感謝の日"); // 勤労感謝の日
}
if(month==12 && counter2==23-1){
printf("天皇誕生日");// 天皇誕生日
}
//変動祝日
if(month==13 && youbi==15){
printf("成人の日");
}
if(month==7 && youbi==15){
printf("海の日");
}
if(month==9 && youbi==15){
printf("敬老の日");
}
if(month==10 && youbi==15){
printf("体育の日");
}
if(month==3 && counter2 == (int)(20.8431 + 0.242194*(year-1980) - (year-1980)/4-1)){
printf("春分の日");
}
if(month==9 && counter2 == (int)(23.2488 + 0.242194*(year-1980) - (year-1980)/4-1)){
printf("秋分の日");
}

}


こちらをコンパイルして出力すると、日付の次の日に祝日名がでてとても見にくいです。
祝日名を適切な日付の下に出力するにはどうすればよいでしょうか。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: C言語で万年カレンダー

#8

投稿記事 by softya(ソフト屋) » 12年前

とりあえずフォーラムルールを一度ご覧ください。codeタグがないと非常に読みづらいので回答が付き辛いです。
http://dixq.net/board/board.html
※ 最後の方に有る義務行為は守って頂けるようにお願いしておきます。分からないことは聞いてください。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

かずま

Re: C言語で万年カレンダー

#9

投稿記事 by かずま » 12年前

コード:

年を入力:2014
月を入力:5 
閏年ではない年  

SUN MON TUE WED TUE FRI SAT 
                 1   2   3  憲法記念日

 4  みどりの日 5  子供の日 6   7   8   9  10  

11  12  13  14  15  16  17  

18  19  20  21  22  23  24  

25  26  27  28  29  30  31  


と表示されますよね。
どのように表示したいんですか?
[code=text]を使って、答えてください。

まさか、次のようなものではないでしょうね。

コード:

SUN MON TUE WED TUE FRI SAT 
                 1   2   3   
                        憲法
 4   5   6   7   8   9  10  
みど子供
11  12  13  14  15  16  17  

18  19  20  21  22  23  24  

25  26  27  28  29  30  31  


さと

Re: C言語で万年カレンダー

#10

投稿記事 by さと » 12年前

ルールーを読んでいなくて

コード:

をつけていませんでした。
申し訳ないです。
次回から気を付けます。

>かずまさん
憲法記念日やみどりの日やこどもの日
と全文表示されるようにしたいのです。
カレンダーの曜日と曜日、数字と数字の間隔をあけなければなりませんでした。

コード:

 
年を入力:2014
月を入力:5 
閏年ではない年 
     SUN         MON         TUE        WED        TUE          FRI        SAT
                                                           1            2           3
                                                                                    憲法記念日
       4           5             6            7            8             9         10
みどりの日 こどもの日
      11           12            13           14           15            16         17

      18           19            20           21           22            23         24

      25           26            27           28           29            30         31
      

 


このような感じの出力結果です。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: C言語で万年カレンダー

#11

投稿記事 by softya(ソフト屋) » 12年前

休日の情報を表示させるフラグを格納した配列を日付の下の行で参照しながら表示するって方法がありますね。

>次回から気を付けます。

今やらないと、見づらいままですよ。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

box
記事: 2002
登録日時: 15年前

Re: C言語で万年カレンダー

#12

投稿記事 by box » 12年前

さと さんが書きました:質問よろしいでしょうか。
このプログラムは詳細設計書などを書きそれをもとに作成されたのでしょうか。
それともいきなり打ち込んで作成されたのでしょうか。
詳細設計といえるほどのことは行ないませんでした。700行程度ですからね。
「こんな風に出力したいなぁ」というメモ書きはしました。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

閉鎖

“C言語何でも質問掲示板” へ戻る