目次
サンプル・プログラム
| daytodaywin.msi | インストーラ |
| bin/daytodaywin.exe | 実行プログラム本体 |
| bin/libcurl-x64.dll | 実行時に必要になるDLL |
| bin/etc/help.chm | ヘルプ・ファイル |
| sour/daytodaywin.cpp | ソース・プログラム |
| sour/resource.h | リソース・ヘッダ |
| sour/resource.rc | リソース・ファイル |
| sour/application.ico | アプリケーション・アイコン |
| sour/mystrings.cpp | 汎用文字列処理関数など(ソース) |
| sour/mystrings.h | 汎用文字列処理関数など(ヘッダ) |
| sour/pahooCache.cpp | キャッシュ処理に関わるクラス(ソース) |
| sour/pahooCache.hpp | キャッシュ処理に関わるクラス(ヘッダ) |
| sour/makefile | ビルド |
| バージョン | 更新日 | 内容 |
|---|---|---|
| 1.5.2 | 2026/05/24 | getDayToday() 「年」取得パターン修正, 使用ライブラリ更新 |
| 1.5.1 | 2026/02/21 | 使用ライブラリ更新 |
| 1.5.0 | 2025/09/11 | getDayToday() キャッシュシステムを利用するよう変更,使用ライブラリ更新 |
| 1.4.6 | 2025/09/11 | getDayToday() 「できごと」取得パターン変更 |
| 1.4.5 | 2025/07/17 | 使用ライブラリ更新, readWebContents引数不足修正 |
使用ライブラリ
リソースの準備
ResEdit を起動し、resource.rc を用意する。
Eclipse に戻り、ソース・プログラム "daytodaywin.cpp" を追加する。
リンカー・フラグを -mwindows -static -lstdc++ -lgcc -lwinpthread -lcurl -lssl -llzma -lz -lws2_32 "C:\pleiades\eclipse\mingw\x86_64-w64-mingw32\bin\libcurl-x64.dll" に設定する。
MSYS2 コマンドラインからビルドするのであれば、"makefile" を利用してほしい。
解説:ヘッダファイル等
daytodaywin.cpp
35: using namespace std;
36: using namespace boost;
37: using namespace boost::property_tree;
38:
39: #define MAKER "pahoo.org" // 作成者
40: #define APPNAME "daytodaywin" // アプリケーション名
41: #define APPNAMEJP "今日は何の日" // アプリケーション名(日本語)
42: #define APPVERSION "1.5.2" // バージョン
43: #define APPYEAR "2020-25" // 作成年・更新日
44: #define REFERENCE "https://www.pahoo.org/e-soul/webtech/cpp01/cpp01-11-01.shtm" // 参考サイト
45:
46: // char*バッファサイズ
47: #define SIZE_BUFF2 5120
48:
49: // ListViewItemの最大文字長:変更不可
50: #define MAX_LISTVIEWITEM 255
51:
52: // リクエストURL(日本記念日協会)(※変更不可)
53: #define KINENBI_URL "https://www.kinenbi.gr.jp/"
54:
55: // 記念日識別値(※変更不可)
56: #define KINENBI_YEAR (-99999)
57:
58: // キャッシュ・ディレクトリ
59: #define DIR_CACHE_WIKIPEDIA "pcache\\"
60:
61: // キャッシュ保持時間(デフォルト;分)(0:キャッシュしない)
62: #define LIFE_CACHE_WIKIPEDIA (7 * 24 * 60)
63:
64: // 現在のインターフェイス
65: static HINSTANCE hInst;
66:
67: // アプリケーション・ウィンドウ
68: HWND hParent;
69:
70: // アプリケーション・ウィンドウ位置
71: unsigned hParent_X, hParent_Y;
72:
73: // エラー・メッセージ格納用
74: string ErrorMessage;
75:
76: // ヘルプ・ファイル
77: #define HELPFILE ".\\etc\\help.chm"
78:
79: // デフォルト保存ファイル名
80: #define SAVEFILE "daytodaywin.csv"
81:
82: // UserAgent
83: string UserAgent;
その他、とくに注意記載が無い限り、定数は自由に変更できる。
解説:データ構造
daytodaywin.cpp
85: // 出来事格納用クラス
86: #define SIZE_EVENTS 500 // 格納上限
87: class _Events {
88: public:
89: wstring category = L""; // 区分
90: int year = 0; // 西暦年
91: wstring era = L""; // 元号
92: wstring event = L""; // 出来事
93: wstring plus = L""; // 追加情報
94: };
95: unique_ptr<_Events> Events[SIZE_EVENTS] = {};
96:
97: // ソート用構造体
98: struct _stEvents {
99: wchar_t* category = NULL; // 区分
100: int *year = NULL; // 西暦年
101: wchar_t* event = NULL; // 出来事
102: size_t id = 0; // Eventsの添字
103: };
104: vector<_stEvents> Vevents;
コンテンツの必要な部分をクラス _Events に収め、このクラスの実体であるオブジェクトへのポインタに unique_p 配列tr を利用する。出来事の数が未知なので、オブジェクトは必要に応じて動的に確保する(つまり、配列の要素数は不定)。
データ要素としては、日本語テキストを格納する要素にはワイド文字列型(wstring型)を割り当てた。西暦年は、intg型で格納しておく。
クラス _Events のメンバのうち、ソート可能な項目(文字列)へのポインタを vectorクラス へコピーする背景については、「C++ で Googleニュース検索」で述べたとおりである。
解説:キャッシュ・システム
このキャッシュ・システムの仕組みについては、「解説:pahooCacheクラスとデータ構造 - C++ で週間天気予報を表示する」をご覧いただきたい。
daytodaywin.cpp
58: // キャッシュ・ディレクトリ
59: #define DIR_CACHE_WIKIPEDIA "pcache\\"
60:
61: // キャッシュ保持時間(デフォルト;分)(0:キャッシュしない)
62: #define LIFE_CACHE_WIKIPEDIA (7 * 24 * 60)
解説:Wikipedia URL取得
daytodaywin.cpp
327: /**
328: * Wikipedia URL取得
329: * @param int month, day 月,日
330: * @param char* url URLを格納
331: * @param size_t sz urlの最大長
332: * @return なし
333: */
334: void getURL_Wikipedia(int month, int day, char *url, size_t sz) {
335: const string wiki = "https://ja.wikipedia.org/wiki/";
336: static char buff1[SIZE_BUFF2 + 1], buff2[SIZE_BUFF2 + 1];
337:
338: snprintf(buff1, SIZE_BUFF2, "%d月%d日", month, day);
339: string s1 = sjis_utf8(buff1);
340:
341: CURL *curl = curl_easy_init();
342: strncpy(buff2, curl_easy_escape(curl, (const char *)s1.c_str(), strlen(s1.c_str())), SIZE_BUFF2);
343:
344:
345: string ss = wiki + (string)buff2;
346: strncpy(url, ss.c_str(), SIZE_BUFF2);
347:
348: curl_easy_cleanup(curl);
349: }
解説:今日は何の日 取得
daytodaywin.cpp
361: /**
362: * 今日は何の日 取得
363: * @param int month, day 月,日
364: * @param bool lfn 脚注を残す(省略時=FALSE)
365: * @return int 情報件数
366: */
367: int getDayToday(int month, int day, bool lfn=FALSE) {
368: // 初期化
369: for (int i = 0; i < SIZE_EVENTS; i++) {
370: Events[i].reset();
371: Events[i] = NULL;
372: }
373:
374: // キャッシュシステム準備
375: pahooCache* pCC;
376: pCC = new pahooCache(LIFE_CACHE_WIKIPEDIA, getMyPath(APPNAME) + DIR_CACHE_WIKIPEDIA, UserAgent);
377:
378: // Wikipediaコンテンツ取得
379: char url[SIZE_BUFF2];
380: string contents = "";
381: getURL_Wikipedia(month, day, (char *)url, SIZE_BUFF2);
382: if (pCC->load(url, &contents) == FALSE) {
383: ErrorMessage = "Wikipediaにアクセスできません:" + _WS(pCC->getError());
384: delete pCC;
385: return FALSE;
386: }
387:
388: // コンテンツの解釈
389: setlocale(LC_ALL, "Japanese");
390: int cnt = -1;
391: stringstream ss;
392: string ss0;
393: wstring mode = L"";
394: wstring ws, ws2;
395: wstringstream wss;
396: static wsmatch mt1, mt2, mt3;
397: static smatch mt4;
398: wregex re1(_SW("<h2[\\s\\S]+id=\"(できごと|出来事)\""));
399: wregex re2(_SW("<h2[\\s\\S]+id=\"誕生日\""));
400: wregex re3(_SW("<h2[\\s\\S]+id=\"忌日\""));
401: // wregex re5(_SW("<li>[^\\>]+([0-9]+(年|世紀).+)<\\/li>"));
402: wregex re5(_SW("<li[^\\>]*>[^\\>]+([0-9]+(年|世紀).+)<\\/li>"));
403: wregex re6(_SW("([^0-9]*)([0-9]+)年(\\s*[\((].[^\\))]+[)\\)].)?[ \\-]+(.+)")); // 年 - 出来事
404: wregex re7(_SW("([^0-9]*)([0-9]+)年(\\s*[\\((].[^\\))]+[)\\)].)?[ \\-]+([^、]+)、?([^(\\(]+)*([^0-9]*)([0-9]*)")); // 年 - 名前、職業(生没年)
405: wregex re8(_SW("<span[\\s\\S]+id=\"フィクションのできごと\">"));
406: wregex re9(_SW("^\\([\\*\\+].")); // 年 - 名前、職業(生没年)
407: wregex re11(_SW("紀元前"));
408: wregex re99(_SW("<span[\\s\\S]+class=\"vector-menu-heading-label\">他言語版")); // 1行読み込み打ち切り
409:
410: ss << contents;
411: while(ss && getline(ss, ss0)) {
412: // 1行をwstring変換
413: ws = _UW(ss0);
414: // 4000文字以上なら処理しない
415: if (ws.length() > 4000) {
416: continue;
417: }
418: // マッチング処理
419: if (regex_search(ws, mt1, re99)) {
420: break;
421: } else if (regex_search(ws, mt1, re1)) {
422: mode = _SW("出来事");
423: } else if (regex_search(ws, mt1, re8)) {
424: mode = _SW("架空");
425: } else if (regex_search(ws, mt1, re2)) {
426: mode = _SW("誕生");
427: } else if (regex_search(ws, mt1, re3)) {
428: mode = _SW("死亡");
429: } else if ((mode.length() != 0) && (regex_search(ws, mt1, re5) == TRUE)) {
430: // HTMLタグ除去
431: ws = wstrip_tags(ws);
432: // 注釈番号除去
433: ws = wstripAnnotations(ws);
434: // 出来事
435: if ((mode == _SW("出来事")) && (regex_search(ws, mt2, re6) == TRUE)) {
436: cnt++;
437: Events[cnt] = make_unique<_Events>();
438: Events[cnt]->category = mode;
439: if (mt2[1].str().length() == 0) {
440: Events[cnt]->year = atoi(_WS(mt2[2].str()).c_str());
441: } else {
442: Events[cnt]->year = 0 - atoi(_WS(mt2[2].str()).c_str());
443: }
444: Events[cnt]->era = mt2[3].str();
445: Events[cnt]->event = mt2[4].str();
446:
447: // 誕生・死亡
448: } else if (regex_search(ws, mt2, re7)) {
449: cnt++;
450: Events[cnt] = make_unique<_Events>();
451: Events[cnt]->category = mode;
452: if (mt2[1].str().length() == 0) {
453: Events[cnt]->year = atoi(_WS(mt2[2].str()).c_str());
454: } else {
455: Events[cnt]->year = 0 - atoi(_WS(mt2[2].str()).c_str());
456: }
457: Events[cnt]->era = mt2[3].str();
458: ws2 = mt2[6].str();
459: if (regex_search(ws2, mt3, re11) == TRUE) {
460: Events[cnt]->plus = L"-" + mt2[7].str();
461: } else if (regex_search(ws2, mt3, re9) == TRUE) {
462: Events[cnt]->plus = mt2[7].str();
463: } else {
464: Events[cnt]->plus = L"";
465: }
466: wss << mt2[4].str() << _SW("(") << mt2[5].str();
467: if (mode == _SW("誕生")) {
468: wss << _SW(")誕生");
469: if (Events[cnt]->plus.length() != 0) {
470: wss << _SW("(〜") << Events[cnt]->plus << _SW("年)");
471: }
472: } else {
473: wss << _SW(")死去");
474: if (Events[cnt]->plus.length() != 0) {
475: wss << _SW("(") << Events[cnt]->plus << _SW("年〜")<< Events[cnt]->year << _SW("年)");
476: }
477: }
478: Events[cnt]->event = wss.str();
479: wss.str(L"");
480: }
481: }
482: }
483: // インスタンス解放
484: delete pCC;
485:
486: return cnt;
487: }
次に、この内容をスクレイピングしていくのだが、今回は、ワイド文字列に対する正規表現を使うことにした。ソースはSJISで書いているので、ユーザーマクロ関数 _SW を使ってワイド文字列に変換し、これを使って正規表現によるパターンマッチングを行う。ちなみに、C++に正規表現が正式導入されたのはC++11からで、Boost C++ライブラリをベースにしている。
| パターン名 | 内容 |
|---|---|
| re1 | できごと |
| re2 | 誕生日 |
| re3 | 忌日 |
| re5 | 西暦年 |
| re6 | 西暦年(出来事) |
| re7 | 年 - 名前、職業(生没年) |
| re8 | フィクションのできごと |
| re9 | 年 - 名前、職業(生没年) |
| re11 | 紀元前の識別 |
| re99 | 1行読み込み打ち切りパターン |
解説:記念日を取得
daytodaywin.cpp
489: /**
490: * 指定月日の記念日を取得する
491: * @param int month, day 月日
492: * @param int cnt 情報を追加する配列の最初の番号
493: * @return int 情報の総数
494: */
495: int getAnniversary(int month, int day, int cnt=0) {
496: // コンテンツ読み込み
497: static char post[SIZE_BUFF2 + 1];
498: snprintf(post, SIZE_BUFF2, "MD=%d&M=%d&D=%d", month, month, day);
499: string contents = "";
500: int httpStatus;
501: bool res = readWebContents(KINENBI_URL, UserAgent, &contents, &httpStatus, post);
502:
503: if (res == FALSE) {
504: ErrorMessage = "日本記念日協会のサイトにアクセスできません.";
505: return (-1);
506: }
507:
508: // スクレイピング
509: wregex re1(_SW("<a\\s+class=\"winDetail\"\\s+href=\"([^\"]+)\"><font[^>]+>([^<]+)</font>"), wregex::icase);
510: static wsmatch mt1;
511:
512: stringstream ss;
513: wstring mode = _SW("記念日");
514: string ss0;
515: wstring ws;
516: ss << contents;
517: while(ss && getline(ss, ss0)) {
518: // 1行をwstring変換
519: ws = _UW(ss0);
520: if (regex_search(ws, mt1, re1)) {
521: Events[cnt] = make_unique<_Events>();
522: Events[cnt]->category = mode;
523: Events[cnt]->event = mt1[2].str();
524: Events[cnt]->year = KINENBI_YEAR;
525: cnt++;
526: }
527: }
528:
529: return cnt;
530: }
「PHPで記念日を表示する」で紹介した関数 scrapingAnniversary と getAnniversary を合体させた。
前述の getDayToday で取り込んだ情報配列 Events に記念日を追加していく。
参考サイト
- PHPで今日は何の日か調べる:ぱふぅ家のホームページ
- PHPで記念日を表示する:ぱふぅ家のホームページ
- WiX によるWindowsインストーラー作成:ぱふぅ家のホームページ
- C++ 開発環境の準備:ぱふぅ家のホームページ

(2026年5月24日)getDayToday() 「年」取得パターン修正, 使用ライブラリ更新
(2026年2月21日)使用ライブラリ更新
(2025年10月25日)getDayToday() キャッシュシステムを利用するよう変更,使用ライブラリ更新
(2025年9月11日)getDayToday() 「できごと」取得パターン変更
(2025年7月17日)不具合修正, 使用ライブラリ更新