【解説】
運命数については、「0011. 運命数(C言語)」を参照。これによって「日付→運命数」を知ることができたが、その逆、「運命数→日付」を知るプログラムへの興味が高まり、作成した。主な作成の指針は、次の通り。
・日付をたどっていき、調べたい運命数と一致する日付を調べるために、「年→月→日」の3重のループ処理とした。
・ループの範囲の指定を簡素化するために、一番外側の年の範囲を「1900年から当年」という可変のものとし、内側の月・日の範囲は、年末日までとなるように固定した(各月末の日は、関数を用いて暦に応じて変化するものとした)。
・結果の表示量は膨大なものになると予測されたため、結果の出力先を、画面のみ・ファイルのみ・画面とファイルの両方の3つから選べるようにした。
・表示する結果の一覧の冒頭に、「【運命数が×となる日】」の表示を加えて、何の結果かが一目で分かるようにした。
・出力結果に行数を付け、さらに縦に並べて同じ種類の項目が同じ位置に表示されるように工夫した。
【untodate.cのソースコード】
/* 運命数から日付を求める */
/* untodate.c(unmeisuu to date) */
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* strlen() */
#include <ctype.h> /* isdigit() */
#include <time.h>
/* 文字列が正しい形式の数値かどうかを判定する関数 */
int isValidNumber(char line[]);
/* year年month月に含まれる日数を返す関数 */
/* isLeapYear()関数を使用する */
int getDaysOfTheMonth(int year,int month);
/* うるう年の判定をする関数 */
int isLeapYear(int year);
/* 運命数を求める関数 */
/* isZorome()関数を使用する */
int unmeisuu(int date);
/* ぞろ目かどうかを判定する関数 */
int isZorome(int n);
/* 現在日付時刻からファイル名を生成する関数 */
void genFileName(char *filenamep);
/* 10進整数の桁数を求める関数 */
int getDigit(int n);
int main(void)
{
char line[256], outputto, ch, zz;
char filenamea[256], *filenamep;
int un, year, month, day, date, i, rows, totalrows;
struct tm *t;
time_t tt;
FILE *foutp;
filenamep = filenamea;
time(&tt);
t = localtime(&tt);
while (1) {
printf("【運命数から日付を求めて表示します】\n");
printf("日付は、1900年1月1日以降今年末までに限ります。\n\n");
while (1) {
printf("調べたい運命数を入力してください。\n");
printf("ただし、次の数に限ります。\n");
printf("1~9の整数、11、22、33、44。\n\n");
printf("運命数 = ? ");
fgets(line, sizeof line, stdin);
sscanf(line, "%d%c", &un, &zz);
line[strlen(line) - 1] = '\0';
if (!isValidNumber(line)) {
printf("無効な入力です。\n");
printf("半角の正の整数値を入力してください。\n\n");
continue;
} else if (un != 1 && un != 2 && un != 3 && un != 4 && un != 5 &&
un != 6 && un != 7 && un != 8 && un != 9 &&
un != 11 && un != 22 && un != 33 && un != 44 ||
zz != '\n') {
printf("無効な入力です。\n");
printf("1~9の整数、11、22、33、44のうちのいずれかを");
printf("入力してください。\n\n");
continue;
} else
break;
}
while (1) {
printf("結果の出力先を1、2、3から選んでください。\n");
printf("1. 画面だけに出力する。\n");
printf("2. ファイルだけに出力する。\n");
printf("3. 画面とファイルの両方に出力する。\n");
fgets(line, sizeof line, stdin);
sscanf(line, "%c%c", &outputto, &zz);
if (outputto != '1' && outputto != '2' && outputto != '3' || zz != '\n') {
printf("無効な入力です。\n");
printf("1、2、3のいずれかを入力してください。\n\n");
continue;
} else
break;
}
/* 「【運命数が△△となる日】」の行を表示するための処理 */
switch (outputto) {
case '1':
printf("【運命数が%dとなる日】\n", un);
break;
case '2':
genFileName(filenamep);
foutp = fopen(filenamep, "w");
if (foutp == NULL) {
printf("ファイルを開けません。終了します。\n");
return EXIT_FAILURE;
}
fprintf(foutp, "【運命数が%dとなる日】\n", un);
break;
case '3':
printf("【運命数が%dとなる日】\n", un);
genFileName(filenamep);
foutp = fopen(filenamep, "w");
if (foutp == NULL) {
printf("ファイルを開けません。終了します。\n");
return EXIT_FAILURE;
}
fprintf(foutp, "【運命数が%dとなる日】\n", un);
break;
default:
break;
}
/* totalrowsの値を取得するためのループ処理 */
rows = 0;
for (year = 1900; year <= 1900+(t->tm_year); year++)
for (month = 1; month <= 12; month++)
for (day = 1; day <= getDaysOfTheMonth(year, month); day++) {
date = 10000 * year + 100 * month + day;
if (un == unmeisuu(date))
rows++;
}
totalrows = rows;
/* 日付の一覧を表示するためのループ処理 */
rows = 0;
for (year = 1900; year <= 1900+(t->tm_year); year++)
for (month = 1; month <= 12; month++)
for (day = 1; day <= getDaysOfTheMonth(year, month); day++) {
date = 10000 * year + 100 * month + day;
if (un == unmeisuu(date)) {
switch (outputto) {
case '1':
rows++;
for (i = 0; i < getDigit(totalrows)-getDigit(rows); i++)
printf(" ");
printf("%d: %4d年%2d月%2d日\n", rows, year, month, day);
break;
case '2':
rows++;
for (i = 0; i < getDigit(totalrows)-getDigit(rows); i++)
fprintf(foutp, " ");
fprintf(foutp, "%d: %4d年%2d月%2d日\n", rows, year, month, day);
break;
case '3':
rows++;
for (i = 0; i < getDigit(totalrows)-getDigit(rows); i++)
printf(" ");
printf("%d: %4d年%2d月%2d日\n", rows, year, month, day);
for (i = 0; i < getDigit(totalrows)-getDigit(rows); i++)
fprintf(foutp, " ");
fprintf(foutp, "%d: %4d年%2d月%2d日\n", rows, year, month, day);
break;
default:
break;
}
}
}
/* 選択肢2、3の場合のファイルを閉じる処理 */
switch (outputto) {
case '1':
break;
case '2':
case '3':
fclose(foutp);
break;
default:
break;
}
/* 操作を続けるかどうかの選択 */
while (1) {
printf("\n続けますか?(y/n) ");
fgets(line, sizeof line, stdin);
sscanf(line, "%c%c", &ch, &zz);
if ((ch == 'y') && (zz == '\n')) {
printf("\n+++++\n\n");
break;
} else if ((ch == 'n') && (zz == '\n'))
return EXIT_SUCCESS;
else {
printf("無効な入力です。\n");
printf("yまたはnを入力してください。\n");
continue;
}
}
}
}
/* 文字列が正しい形式の数値かどうかを判定する関数 */
int isValidNumber(char line[])
{
int i = 0;
/* 最初は符号(省略可) */
if (line[i] == '+')
i++;
/* 次に数字(省略不可) */
if (!isdigit(line[i]))
return 0;
i++;
/* 次に数字列(省略可) */
while (isdigit(line[i]))
i++;
return line[i] == '\0';
}
/* year年month月に含まれる日数を返す関数 */
/* isLeapYear()関数を使用する */
int getDaysOfTheMonth(int year,int month)
{
int days;
if (isLeapYear(year) && (month == 2)) /* うるう年の2月 */
days = 29;
else if ((!isLeapYear(year)) && (month == 2)) /* うるう年ではない2月 */
days = 28;
else if ((month == 4) || (month == 6) || /* 4月、6月、9月、11月 */
(month == 9) || (month == 11))
days = 30;
else if ((month == 1) || (month == 3) || /* 1、3、5、7、8、10、12月 */
(month == 5) || (month == 7) ||
(month == 8) || (month == 10) ||
(month == 12))
days = 31;
else { /* 上記以外 */
printf("月の日数の計算ができません。処理を終了します。\n");
exit(EXIT_FAILURE);
}
return days;
}
/* うるう年の判定をする関数 */
int isLeapYear(int year)
{
return ((year % 400 == 0) || ((year % 100) && (year % 4 == 0)));
}
/* 運命数を求める関数 */
/* isZorome()関数を使用する */
int unmeisuu(int date)
{
int sum = 0;
while (date > 0) {
sum += date % 10;
date /= 10;
}
if (isZorome(sum))
return sum;
if (sum > 9)
sum = unmeisuu(sum);
return sum;
}
/* ぞろ目かどうかを判定する関数 */
int isZorome(int n)
{
int m;
/* 1桁の場合、ぞろ目ではないので偽を返す */
if (!(n/10))
return 0;
m = n % 10; /* 1の位を記憶 */
while (1) { /* 繰り返し */
n /= 10; /* 10で割って1未満を切り捨て */
if (!n) /* nが0になったら */
break; /* 繰り返しをやめる */
if (m != n % 10) /* 1の位とある位を比較して違っていたら */
return 0; /* 偽を返す */
}
return 1; /* nが0になるまですべての位が同じなら真を返す */
}
/* 現在日付時刻からファイル名を生成する関数 */
void genFileName(char *filenamep)
{
struct tm *t;
time_t tt;
char snumrand[10];
time(&tt);
t = localtime(&tt);
srand((unsigned int)time(NULL));
rand(); rand(); rand(); rand(); rand();
sprintf(filenamep, "untodate%04d%02d%02d_%02d%02d%02d_",
1900+(t->tm_year), 1+(t->tm_mon), t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec);
sprintf(snumrand, "%04d", rand() % 10000);
filenamep = strcat(filenamep, snumrand);
filenamep = strcat(filenamep, ".txt");
}
/* 10進整数の桁数を求める関数 */
int getDigit(int n)
{
int digit = 0;
while(n){
n /= 10;
digit++;
}
return digit;
}
D:\test>untodate
【運命数から日付を求めて表示します】
日付は、1900年1月1日以降今年末までに限ります。
調べたい運命数を入力してください。
ただし、次の数に限ります。
1~9の整数、11、22、33、44。
運命数 = ? 1
結果の出力先を1、2、3から選んでください。
1. 画面だけに出力する。
2. ファイルだけに出力する。
3. 画面とファイルの両方に出力する。
2
続けますか?(y/n) y
+++++
(中略)
+++++
【運命数から日付を求めて表示します】
日付は、1900年1月1日以降今年末までに限ります。
調べたい運命数を入力してください。
ただし、次の数に限ります。
1~9の整数、11、22、33、44。
運命数 = ? 9
結果の出力先を1、2、3から選んでください。
1. 画面だけに出力する。
2. ファイルだけに出力する。
3. 画面とファイルの両方に出力する。
2
続けますか?(y/n) y
+++++
【運命数から日付を求めて表示します】
日付は、1900年1月1日以降今年末までに限ります。
調べたい運命数を入力してください。
ただし、次の数に限ります。
1~9の整数、11、22、33、44。
運命数 = ? 11
結果の出力先を1、2、3から選んでください。
1. 画面だけに出力する。
2. ファイルだけに出力する。
3. 画面とファイルの両方に出力する。
2
続けますか?(y/n) y
+++++
【運命数から日付を求めて表示します】
日付は、1900年1月1日以降今年末までに限ります。
調べたい運命数を入力してください。
ただし、次の数に限ります。
1~9の整数、11、22、33、44。
運命数 = ? 22
結果の出力先を1、2、3から選んでください。
1. 画面だけに出力する。
2. ファイルだけに出力する。
3. 画面とファイルの両方に出力する。
2
続けますか?(y/n) y
+++++
【運命数から日付を求めて表示します】
日付は、1900年1月1日以降今年末までに限ります。
調べたい運命数を入力してください。
ただし、次の数に限ります。
1~9の整数、11、22、33、44。
運命数 = ? 33
結果の出力先を1、2、3から選んでください。
1. 画面だけに出力する。
2. ファイルだけに出力する。
3. 画面とファイルの両方に出力する。
2
続けますか?(y/n) y
+++++
【運命数から日付を求めて表示します】
日付は、1900年1月1日以降今年末までに限ります。
調べたい運命数を入力してください。
ただし、次の数に限ります。
1~9の整数、11、22、33、44。
運命数 = ? 44
結果の出力先を1、2、3から選んでください。
1. 画面だけに出力する。
2. ファイルだけに出力する。
3. 画面とファイルの両方に出力する。
2
続けますか?(y/n) n
D:\test>dir
(中略)
2022-10-18 20:39 148,480 untodate.exe
2022-10-18 20:42 109,825 untodate20221018_204204_4345.txt ←運命数が1~9、11、22、33、44の13個のテキストファイルが出力された。
2022-10-18 20:42 29,261 untodate20221018_204207_3357.txt
2022-10-18 20:42 109,759 untodate20221018_204210_5137.txt
2022-10-18 20:42 72,337 untodate20221018_204214_0844.txt
2022-10-18 20:42 109,803 untodate20221018_204217_9856.txt
2022-10-18 20:42 67,585 untodate20221018_204220_1636.txt
2022-10-18 20:42 109,869 untodate20221018_204223_0648.txt
2022-10-18 20:42 108,505 untodate20221018_204227_6355.txt
2022-10-18 20:42 109,891 untodate20221018_204229_4209.txt
2022-10-18 20:42 80,544 untodate20221018_204232_3221.txt
2022-10-18 20:42 37,512 untodate20221018_204235_5001.txt
2022-10-18 20:42 42,308 untodate20221018_204237_2855.txt
2022-10-18 20:42 1,324 untodate20221018_204240_1867.txt
【運命数が1となる日】
1: 1900年 1月 8日
2: 1900年 1月17日
3: 1900年 1月26日
4: 1900年 2月 7日
5: 1900年 2月16日
(中略)
4987: 2022年11月29日
4988: 2022年12月 1日
4989: 2022年12月10日
4990: 2022年12月19日
4991: 2022年12月28日
:
【運命数が44となる日】
1: 1959年 9月29日
2: 1968年 9月29日
3: 1969年 8月29日
4: 1969年 9月19日
5: 1969年 9月28日
(中略)
61: 1999年 8月17日
62: 1999年 8月26日
63: 1999年 9月 7日
64: 1999年 9月16日
65: 1999年 9月25日