目次
サンプル・プログラム
| nengowin.msi | インストーラ |
| bin/nengowin.exe | 実行プログラム本体(GUI版) |
| bin/nmtxt.exe | 実行プログラム本体(CUI版) |
| bin/option.txt | 変換オプションを保存するファイル |
| bin/etc/era.txt | 西暦⇔元号変換表 |
| bin/etc/help.chm | ヘルプ・ファイル |
| sour/nengowin.cpp | ソース・プログラム |
| sour/resource.h | リソース・ヘッダ |
| sour/resource.rc | リソース・ファイル(GUI版) |
| sour/resource2.rc | リソース・ファイル(CUI版) |
| sour/application.ico | アプリケーション・アイコン(GUI版) |
| sour/application2.ico | アプリケーション・アイコン(CUI版) |
| sour/pahooNormalizeText.cpp | テキスト正規化クラス(ソース) |
| sour/pahooNormalizeText.hpp | テキスト正規化クラス(ヘッダ) |
| sour/makefile | GUI版ビルド |
| sour/makefile_cmd | CUI版ビルド |
| バージョン | 更新日 | 内容 |
|---|---|---|
| 1.2.10 | 2026/03/15 | 使用ライブラリ更新, optional名前衝突回避 |
| 1.2.9 | 2025/11/23 | 使用ライブラリ更新 |
| 1.2.8 | 2025/08/02 | 使用ライブラリ更新 |
| 1.2.7 | 2025/03/29 | 使用ライブラリ更新 |
| 1.2.5 | 2024/08/31 | 使用ライブラリ更新 |
| バージョン | 更新日 | 内容 |
|---|---|---|
| 2.0.1 | 2025/11/23 | bug-fix: 変数variable未使用ワーニング対策 |
| 2.0.0 | 2025/08/30 | 表記ゆれ統一機能を追加 |
| 1.9.1 | 2023/12/17 | pahooNormalizeText() - MECAB非使用時の対策 |
| 1.9.0 | 2023/10/25 | normalizeText() - 二重引用符の開閉処理 |
| 1.8.0 | 2023/10/25 | pahooNormalizeText() - MeCab動作チェック追加 |
使用ライブラリ
リソースの準備
ResEdit を起動し、resource.rc を用意する。
Eclipse に戻り、ソース・プログラム "nengowin.cpp" を追加する。
リンカー・フラグを -mwindows -static-libstdc++ -static-libgcc -lpthread -lwinpthread -static "(任意のパス)\libboost_program_options-mt.dll" -static "(任意のパス)\libmecab-2.dll" に設定する。
また、CUI版をビルドするために、構成 CMD を追加し、リンカー・フラグを -static-libstdc++ -static-libgcc -lpthread -static "(任意のパス)\libboost_program_options-mt.dll" -static "(任意のパス)\libmecab-2.dll"" に設定する。
MSYS2 コマンドラインからビルドするのであれば、"makefile" と "makefile_cmd" を利用してほしい。
プログラムの流れ
解説:テキスト正規化クラス
pahooNormalizeText.hpp
56: // 和暦・西暦年号変換オプション
57: #define OPTION_MODE_AD 'A' // 西暦に統一
58: #define OPTION_MODE_ERA 'W' // 和暦に統一
59: #define OPTION_MODE_ADERA 'C' // 西暦(和暦)
60: #define OPTION_SCOPE_1 '1' // 奈良時代以降
61: #define OPTION_SCOPE_2 '2' // 明治以降
62: #define OPTION_SCOPE_0 '0' // 変換しない
63: #define OPTION_AD_HAN 'h' // 数字変換(西暦):半角数字
64: #define OPTION_AD_ZEN 'z' // 同上 :全角数字
65: #define OPTION_AD_KAN1 'k' // 同上 :漢数字(単純)
66: #define OPTION_AD_KAN2 'l' // 同上 :漢数字
67: #define OPTION_ERA_HAN 'H' // 数字変換(和暦):半角数字
68: #define OPTION_ERA_ZEN 'Z' // 同上 :全角数字
69: #define OPTION_ERA_KAN1 'K' // 同上 :漢数字(単純)
70: #define OPTION_ERA_KAN2 'L' // 同上 :漢数字
71:
72: // 文字列置換定義ファイル
73: #define FRONT_REPLACE_CSV ".\\etc\\frontrep.csv"
74:
75: // 表記ゆれ統一辞書ファイル
76: #define VARIABILITY_REPLACE_CSV ".\\etc\\vardic.csv"
77:
78: // 年号変換オプションの初期値
79: #define CONVOPT_INIT "C1hH"
解説:西暦⇔元号変換
pahooNormalizeText.cpp
1335: /**
1336: * 西暦⇔元号変換
1337: * @param wstring sour オリジナル・テキスト
1338: * @param char* option 変換オプション
1339: * @return wstring 変換後テキスト
1340: */
1341: wstring pahooNormalizeText::convNengo(wstring wsour, const char* option) {
1342: wstring wdest = L"";
1343:
1344: // 変換範囲
1345: if (strchr(option, OPTION_SCOPE_1) != NULL) {
1346: setScope(START_SCOPE1, END_SCOPE);
1347: } else if (strchr(option, OPTION_SCOPE_2) != NULL) {
1348: setScope(START_SCOPE2, END_SCOPE);
1349: } else if (strchr(option, OPTION_SCOPE_0) != NULL) {
1350: setScope(END_SCOPE, END_SCOPE);
1351: }
1352:
1353: // 西暦に統一
1354: if (strchr(option, OPTION_MODE_AD) != NULL) {
1355: wdest = this->seireki(wsour, option, TRUE);
1356: // 和暦に統一
1357: } else if (strchr(option, OPTION_MODE_ERA) != NULL) {
1358: wdest = this->wareki(wsour, option, TRUE);
1359: // 西暦(和暦)
1360: } else if (strchr(option, OPTION_MODE_ADERA) != NULL) {
1361: wdest = this->mixture(wsour, option, TRUE);
1362: }
1363: // 月日
1364: wdest = this->monthday(wdest, option);
1365:
1366: return wdest;
1367: }
解説:西暦に統一
pahooNormalizeText.cpp
1197: /**
1198: * 西暦に統一
1199: * @param wstring sour オリジナル・テキスト
1200: * @param char* option 変換オプション
1201: * @param bool flag TRUE=エスケープ文字を消去/FALSE=消去しない
1202: * @return wstring 変換後テキスト
1203: */
1204: wstring pahooNormalizeText::seireki(wstring wsour, const char* option=NULL, bool flag=TRUE) {
1205: wregex re(_SW("([^0-90-9〇一二三四五六七八九十\百千万あ-ん、-?!-¥]{0,4})\\s*([0-90-9〇元一二三四五六七八九十\百千万]+)年\\s*([0-90-9〇一二三四五六七八九十\]*)月?([0-90-9〇一二三四五六七八九十\]*)日?"));
1206: wregex re2(ESCYEAR);
1207: wsmatch mt;
1208:
1209: wstring wdest = L"";
1210: wstring suffix = L"";
1211: for (bool ismatch = regex_search(wsour, mt, re); ismatch != FALSE;
1212: ismatch = regex_search(mt[0].second, mt.suffix().second, mt, re)) {
1213: if (flag) {
1214: wdest += (wstring)mt.prefix() +
1215: regex_replace(this->wareki2seireki(mt, option), re2, L"");
1216: } else {
1217: wdest += (wstring)mt.prefix() + this->wareki2seireki(mt, option);
1218: }
1219: suffix = (wstring)mt.suffix(); // 未変換の部分文字列
1220: }
1221: wdest += suffix;
1222:
1223: return wdest;
1224: }
pahooNormalizeText.cpp
1170: /**
1171: * 和暦→西暦変換(漢数字対応)
1172: * @param wsmatch mt 年月日パターンにマッチしたテキスト
1173: * @return wstring 西暦
1174: */
1175: wstring pahooNormalizeText::wareki2seireki(wsmatch mt, const char* option=NULL) {
1176: wstring year, month, day;
1177:
1178: if (mt[2].str() == _SW("元")) {
1179: year = L"1";
1180: } else if (mt[2].str() != L"") {
1181: year = this->kan2num(mt[2].str(), 0);
1182: }
1183: if (mt[3].str() != L"") {
1184: month = this->kan2num(mt[3].str(), 0);
1185: } else {
1186: month = _SW("0");
1187: }
1188: if (mt[4].str() != L"") {
1189: day = this->kan2num(mt[4].str(), 0);
1190: } else {
1191: day = _SW("0");
1192: }
1193:
1194: return this->era2ad(mt[1].str(), stoi(year), stoi(month), stoi(day), option);
1195: }
解説:和暦に統一
pahooNormalizeText.cpp
1141: /**
1142: * 和暦に統一
1143: * @param wstring sour オリジナル・テキスト
1144: * @param char* option 変換オプション
1145: * @param bool flag TRUE=エスケープ文字を消去/FALSE=消去しない
1146: * @return wstring 変換後テキスト
1147: */
1148: wstring pahooNormalizeText::wareki(wstring wsour, const char* option=NULL, bool flag=TRUE) {
1149: wregex re(_SW("([^0-90-9〇一二三四五六七八九十\百千万あ-ん、-?!-¥]{0,4})\\s*([0-90-9元〇一二三四五六七八九十\百千万]+)年\\s*([0-90-9〇一二三四五六七八九十\]*)月?([0-90-9〇一二三四五六七八九十\]*)日?"));
1150: wregex re2(ESCYEAR);
1151: wsmatch mt;
1152:
1153: wstring wdest = L"";
1154: wstring suffix = L"";
1155: for (bool ismatch = regex_search(wsour, mt, re); ismatch != FALSE;
1156: ismatch = regex_search(mt[0].second, mt.suffix().second, mt, re)) {
1157: if (flag) {
1158: wdest += (wstring)mt.prefix() +
1159: regex_replace(this->seireki2wareki(mt, option), re2, L"");
1160: } else {
1161: wdest += (wstring)mt.prefix() + this->seireki2wareki(mt, option);
1162: }
1163: suffix = (wstring)mt.suffix(); // 未変換の部分文字列
1164: }
1165: wdest += suffix;
1166:
1167: return wdest;
1168: }
pahooNormalizeText.cpp
1110: /**
1111: * 西暦→和暦変換(漢数字対応)
1112: * @param wsmatch mt 年月日パターンにマッチしたテキスト
1113: * @return wstring 和暦
1114: */
1115: wstring pahooNormalizeText::seireki2wareki(wsmatch mt, const char* option=NULL) {
1116: wstring year, month, day;
1117:
1118: if (mt[2].str() != L"") {
1119: if (mt[2].str() == _SW("元")) {
1120: year = L"1";
1121: } else {
1122: year = this->kan2num(mt[2].str(), 0);
1123: }
1124: } else {
1125: year = _SW("0");
1126: }
1127: if (mt[3].str() != L"") {
1128: month = this->kan2num(mt[3].str(), 0);
1129: } else {
1130: month = _SW("0");
1131: }
1132: if (mt[4].str() != L"") {
1133: day = this->kan2num(mt[4].str(), 0);
1134: } else {
1135: day = _SW("0");
1136: }
1137:
1138: return this->ad2era(mt[1].str(), stoi(year), stoi(month), stoi(day), option);
1139: }
解説:西暦(和暦)混合変換
pahooNormalizeText.cpp
1257: /**
1258: * 西暦(和暦)混合変換
1259: * @param wstring sour オリジナル・テキスト
1260: * @param char* option 変換オプション
1261: * @param bool flag TRUE=エスケープ文字を消去/FALSE=消去しない
1262: * @return wstring 変換後テキスト
1263: */
1264: wstring pahooNormalizeText::mixture(wstring wsour, const char* option=NULL, bool flag=TRUE) {
1265: wregex re(_SW("([^0-90-9〇一二三四五六七八九十\百千万あ-ん、-?!-¥]{0,4})([0-9元]+)年"));
1266: wregex re2(ESCYEAR);
1267: wsmatch mt;
1268: static const char opt[] = { OPTION_ERA_HAN, 0x00 };
1269:
1270: wstring wstr = this->wareki(wsour, opt, FALSE); // 和暦(半角数字)に統一
1271:
1272: wstring wdest = L"";
1273: wstring suffix = L"";
1274: for (bool ismatch = regex_search(wstr, mt, re); ismatch != FALSE;
1275: ismatch = regex_search(mt[0].second, mt.suffix().second, mt, re)) {
1276: if (flag) {
1277: wdest += (wstring)mt.prefix() +
1278: regex_replace(this->seireki2mix(mt, option), re2, L"");
1279: } else {
1280: wdest += (wstring)mt.prefix() + this->seireki2mix(mt, option);
1281: }
1282: suffix = (wstring)mt.suffix(); // 未変換の部分文字列
1283: }
1284: wdest += suffix;
1285:
1286: return wdest;
1287: }
pahooNormalizeText.cpp
1226: /**
1227: * 和暦→西暦(和暦)変換
1228: * @param wsmatch mt 年月日パターンにマッチしたテキスト
1229: * @param char* option 変換オプション
1230: * @return wstring 西暦
1231: */
1232: wstring pahooNormalizeText::seireki2mix(wsmatch mt, const char* option=NULL) {
1233: wstring wdest = L"";
1234: wstring wstr = L"";
1235: wstring ad = this->wareki2seireki(mt, option);
1236:
1237: if (strchr(option, OPTION_ERA_ZEN) != NULL) {
1238: wstr = this->han2zen(mt[2].str(), _decimal);
1239: } else if (strchr(option, OPTION_ERA_KAN1) != NULL) {
1240: wstr = this->num2kanSimple(mt[2].str());
1241: } else if (strchr(option, OPTION_ERA_KAN2) != NULL) {
1242: wstr = this->num2kan(mt[2].str());
1243: } else {
1244: wstr = mt[2].str();
1245: }
1246: wstring wareki = mt[1].str() + wstr + _SW("年");
1247:
1248: if (ad != wareki) {
1249: wdest = ad + _SW("(") + wareki + _SW(")");
1250: } else {
1251: wdest = ad;
1252: }
1253:
1254: return wdest;
1255: }
解説:西暦⇔元号変換表
era.txt
1: //西暦⇔元号変換表
2: //
3: //書式 'yyyymmdd' => '元号' yyyymmddは当該元号の開始日(西暦年月日)
4:
5: //飛鳥時代
6: '06450717' => '大化',
7: '06500322' => '白雉',
8: '06541124' => '', //空白期間
9: '06860814' => '朱鳥',
10: '06861001' => '', //空白期間
変換表はアプリケーション起動時に readTableEra メソッドを使って読み込む。ファイルの内容を書き換えれば、再コンパイルすることなく変換ルールを変更できる。
同梱する変換表では、南北朝時代は南朝の元号を採用している。参考として、北朝の元号をコメントアウトしてあり、南朝と入れ換えることができる。
本アプリケーションでは旧暦/新暦の変換計算を行っていない。このため、月日は西暦という前提である。「明治5年12月2日」は旧暦なので、「1872年12月31日」と変換すべきだが、「1872年12月2日」に変換する。
そこで、変換条件として「新暦以降」(1873年1月1日以降)を指定できるようにしている。。「新暦以降」を指定して西暦変換すると、明治5年12月2日は変換しないが、明治6年1月1日は1873年1月1日に変換する。
新暦から旧暦への変換については、「旧暦の計算 - PHPで万年カレンダーを作る」を参考にしていただきたい。
解説:定数など
nengowin.cpp
37: // 定数など ==================================================================
38: #define MAKER "pahoo.org" // 作成者
39: #define APPNAME "nengowin" // アプリケーション名
40: #define APPNAMEJP "和暦・西暦年号変換" // アプリケーション名(日本語)
41: #define APPVERSION "1.2.10" // バージョン
42: #define APPYEAR "2020-26" // 作成年
43: #define REFERENCE "https://www.pahoo.org/e-soul/webtech/cpp01/cpp01-19-01.shtm" // 参考サイト
44:
45: // ヘルプ・ファイル
46: #define HELPFILE ".\\etc\\help.chm"
47:
48: // デフォルト保存ファイル名
49: #define SAVEFILE "nengowin.txt"
50:
51: // 西暦⇔元号変換ファイル名
52: #define TABLEERA_FNAME "etc/era.txt"
53:
54: // オプション保存ファイル名:変更不可
55: #define OPTIONFILE "option.txt"
56:
57: // 現在のインターフェイス
58: HINSTANCE hInst;
59:
60: // アプリケーション・ウィンドウ
61: HWND hParent;
62:
63: // 変換プション
64: string Option;
65:
66: // 変換オプションの初期値
67: #define NOPTION_INIT "C1hH"
68:
69: // アプリケーション・ウィンドウ位置
70: unsigned hParent_X, hParent_Y;
71:
72: // エラー・メッセージ格納用【変更不可】
73: string ErrorMessage;
74:
75: // pahooNormalizeTextオブジェクト
76: pahooNormalizeText* pNT;
77:
78: // char*バッファサイズ
79: #define SIZE_BUFF 512
80:
81: // 変換元テキスト(初期値)
82: #define DEF_SOUR "慶応四年一月三日、鳥羽・伏見の戦いが起きる。10月23日、明治天皇が即位。改元は1868年1月25日に遡って適用された。明治2年5月17日、五稜郭が開放され、戊辰戦争は終わる。"
とくに注意記載が無い限り、定数は自由に変更できる。
解説:変換オプション取得・設定
nengowin.cpp
467: /**
468: * 変換オプションを設定する
469: * @param HWND hDlg ウィンドウ・ハンドラ
470: * @param string opt 変換オプション
471: * @return なし
472: */
473: void setOption(HWND hDlg, string opt) {
474: for (int i = 0; i < (int)opt.length(); i++) {
475: char* ch = (char*)opt.substr(i, 1).c_str();
476: switch (*ch) {
477: // 変換方式
478: case OPTION_MODE_AD:
479: SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_AD),
480: BM_SETCHECK, BST_CHECKED, 0);
481: SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_ERA),
482: BM_SETCHECK, BST_UNCHECKED, 0);
483: SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_ADERA),
484: BM_SETCHECK, BST_UNCHECKED, 0);
485: break;
486: case OPTION_MODE_ERA:
487: SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_AD),
488: BM_SETCHECK, BST_UNCHECKED, 0);
489: SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_ERA),
490: BM_SETCHECK, BST_CHECKED, 0);
491: SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_ADERA),
492: BM_SETCHECK, BST_UNCHECKED, 0);
493: break;
494: case OPTION_MODE_ADERA:
495: SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_AD),
496: BM_SETCHECK, BST_UNCHECKED, 0);
497: SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_ERA),
498: BM_SETCHECK, BST_UNCHECKED, 0);
499: SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_ADERA),
500: BM_SETCHECK, BST_CHECKED, 0);
501: break;
502: // 変換範囲
503: case OPTION_SCOPE_1:
504: SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_1),
505: BM_SETCHECK, BST_CHECKED, 0);
506: SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_2),
507: BM_SETCHECK, BST_UNCHECKED, 0);
508: SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_0),
509: BM_SETCHECK, BST_UNCHECKED, 0);
510: break;
511: case OPTION_SCOPE_2:
512: SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_1),
513: BM_SETCHECK, BST_UNCHECKED, 0);
514: SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_2),
515: BM_SETCHECK, BST_CHECKED, 0);
516: SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_0),
517: BM_SETCHECK, BST_UNCHECKED, 0);
518: break;
519: case OPTION_SCOPE_0:
520: SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_1),
521: BM_SETCHECK, BST_UNCHECKED, 0);
522: SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_2),
523: BM_SETCHECK, BST_UNCHECKED, 0);
524: SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_0),
525: BM_SETCHECK, BST_CHECKED, 0);
526: break;
527: // 数字変換(西暦)
528: case OPTION_AD_HAN:
529: SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_HAN),
530: BM_SETCHECK, BST_CHECKED, 0);
531: SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_ZEN),
532: BM_SETCHECK, BST_UNCHECKED, 0);
533: SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_KAN1),
534: BM_SETCHECK, BST_UNCHECKED, 0);
535: SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_KAN2),
536: BM_SETCHECK, BST_UNCHECKED, 0);
537: break;
538: case OPTION_AD_ZEN:
539: SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_HAN),
540: BM_SETCHECK, BST_UNCHECKED, 0);
541: SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_ZEN),
542: BM_SETCHECK, BST_CHECKED, 0);
543: SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_KAN1),
544: BM_SETCHECK, BST_UNCHECKED, 0);
545: SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_KAN2),
546: BM_SETCHECK, BST_UNCHECKED, 0);
547: break;
548: case OPTION_AD_KAN1:
549: SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_HAN),
550: BM_SETCHECK, BST_UNCHECKED, 0);
551: SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_ZEN),
552: BM_SETCHECK, BST_UNCHECKED, 0);
553: SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_KAN1),
554: BM_SETCHECK, BST_CHECKED, 0);
555: SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_KAN2),
556: BM_SETCHECK, BST_UNCHECKED, 0);
557: break;
558: case OPTION_AD_KAN2:
559: SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_HAN),
560: BM_SETCHECK, BST_UNCHECKED, 0);
561: SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_ZEN),
562: BM_SETCHECK, BST_UNCHECKED, 0);
563: SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_KAN1),
564: BM_SETCHECK, BST_UNCHECKED, 0);
565: SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_KAN2),
566: BM_SETCHECK, BST_CHECKED, 0);
567: break;
568: // 数字変換(和暦)
569: case OPTION_ERA_HAN:
570: SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_HAN),
571: BM_SETCHECK, BST_CHECKED, 0);
572: SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_ZEN),
573: BM_SETCHECK, BST_UNCHECKED, 0);
574: SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_KAN1),
575: BM_SETCHECK, BST_UNCHECKED, 0);
576: SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_KAN2),
577: BM_SETCHECK, BST_UNCHECKED, 0);
578: break;
579: case OPTION_ERA_ZEN:
580: SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_HAN),
581: BM_SETCHECK, BST_UNCHECKED, 0);
582: SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_ZEN),
583: BM_SETCHECK, BST_CHECKED, 0);
584: SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_KAN1),
585: BM_SETCHECK, BST_UNCHECKED, 0);
586: SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_KAN2),
587: BM_SETCHECK, BST_UNCHECKED, 0);
588: break;
589: case OPTION_ERA_KAN1:
590: SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_HAN),
591: BM_SETCHECK, BST_UNCHECKED, 0);
592: SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_ZEN),
593: BM_SETCHECK, BST_UNCHECKED, 0);
594: SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_KAN1),
595: BM_SETCHECK, BST_CHECKED, 0);
596: SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_KAN2),
597: BM_SETCHECK, BST_UNCHECKED, 0);
598: break;
599: case OPTION_ERA_KAN2:
600: SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_HAN),
601: BM_SETCHECK, BST_UNCHECKED, 0);
602: SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_ZEN),
603: BM_SETCHECK, BST_UNCHECKED, 0);
604: SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_KAN1),
605: BM_SETCHECK, BST_UNCHECKED, 0);
606: SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_KAN2),
607: BM_SETCHECK, BST_CHECKED, 0);
608: break;
609: default:
610: break;
611: }
612: }
613: }
nengowin.cpp
615: /**
616: * 変換オプションを取得する
617: * @param HWND hDlg ウィンドウ・ハンドラ
618: * @return string 変換オプション
619: */
620: string getOption(HWND hDlg) {
621: string opt = "";
622: // 変換方式
623: if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_AD), BM_GETCHECK, 0, 0)) {
624: opt += OPTION_MODE_AD;
625: } else if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_ERA), BM_GETCHECK, 0, 0)) {
626: opt += OPTION_MODE_ERA;
627: } else if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_MODE_ADERA), BM_GETCHECK, 0, 0)) {
628: opt += OPTION_MODE_ADERA;
629: }
630: // 変換範囲
631: if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_1), BM_GETCHECK, 0, 0)) {
632: opt += OPTION_SCOPE_1;
633: } else if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_2), BM_GETCHECK, 0, 0)) {
634: opt += OPTION_SCOPE_2;
635: } else if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_SCOPE_0), BM_GETCHECK, 0, 0)) {
636: opt += OPTION_SCOPE_0;
637: }
638: // 数字変換(西暦)
639: if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_HAN), BM_GETCHECK, 0, 0)) {
640: opt += OPTION_AD_HAN;
641: } else if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_ZEN), BM_GETCHECK, 0, 0)) {
642: opt += OPTION_AD_ZEN;
643: } else if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_KAN1), BM_GETCHECK, 0, 0)) {
644: opt += OPTION_AD_KAN1;
645: } else if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_AD_KAN2), BM_GETCHECK, 0, 0)) {
646: opt += OPTION_AD_KAN2;
647: }
648: // 数字変換(和暦)
649: if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_HAN), BM_GETCHECK, 0, 0)) {
650: opt += OPTION_ERA_HAN;
651: } else if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_ZEN), BM_GETCHECK, 0, 0)) {
652: opt += OPTION_ERA_ZEN;
653: } else if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_KAN1), BM_GETCHECK, 0, 0)) {
654: opt += OPTION_ERA_KAN1;
655: } else if (SendMessage(GetDlgItem(hDlg, IDC_RADIO_ERA_KAN2), BM_GETCHECK, 0, 0)) {
656: opt += OPTION_ERA_KAN2;
657: }
658:
659: return opt;
660: }
解説:変換オプション読込・保存・削除
nengowin.cpp
136: /**
137: * AppDataのパスを取得
138: * @param char* appname アプリケーション名
139: * @return string パス
140: */
141: string getMyPath(const char* appname) {
142: static TCHAR myPath[MAX_PATH] = "";
143:
144: if (strlen(myPath) == 0) {
145: if (SHGetSpecialFolderPath(NULL, myPath, CSIDL_APPDATA, 0)) {
146: TCHAR *ptmp = _tcsrchr(myPath, _T('\\'));
147: if (ptmp != NULL) {
148: ptmp = _tcsinc(ptmp);
149: *ptmp = _T('\0');
150: }
151: strcat(myPath, _T("Roaming"));
152: CreateDirectory((LPCTSTR)myPath, NULL);
153: strcat(myPath, _T("\\pahoo.org"));
154: CreateDirectory((LPCTSTR)myPath, NULL);
155: strcat(myPath, _T("\\"));
156: strcat(myPath, _T(appname));
157: CreateDirectory((LPCTSTR)myPath, NULL);
158: strcat(myPath, _T("\\"));
159: } else {
160: }
161: }
162: return (string)myPath;
163: }
nengowin.cpp
176: /**
177: * パラメータの読み込み
178: * @param なし
179: * @return なし
180: */
181: void loadParameter(void) {
182: ptree pt;
183:
184: // 初期値設定
185: initParameter();
186:
187: // XMLファイル読み込み
188: try {
189: xml_parser::read_xml(getMyPath(APPNAME) + APPNAME + ".xml", pt);
190:
191: // XML解釈
192: try {
193: // 形式チェック
194: if (boost::optional<string>str = pt.get_optional<string>("parameter")) {
195: } else {
196: return;
197: }
198: // パラメータ読み込み
199: for (auto it : pt.get_child("parameter")) {
200: string type= it.second.get_optional<string>("<xmlattr>.type").value();
201: if (type == "option") {
202: Option = (string)it.second.data();
203: } else if (type == "wx") {
204: hParent_X = (unsigned)stoi(it.second.data());
205: } else if (type == "wy") {
206: hParent_Y = (unsigned)stoi(it.second.data());
207: }
208: }
209: // 解釈失敗したら初期値設定
210: } catch (xml_parser_error& e) {
211: initParameter();
212: return;
213: }
214: // 読み込み失敗したら初期値設定
215: } catch (xml_parser_error& e) {
216: initParameter();
217: return;
218: }
219:
220: // アプリケーション・ウィンドウの位置(デスクトップ範囲外なら原点移動)
221: HWND hDesktop = GetDesktopWindow();
222: WINDOWINFO windowInfo;
223: windowInfo.cbSize = sizeof(WINDOWINFO);
224: GetWindowInfo(hDesktop, &windowInfo);
225: if (hParent_X >= (unsigned)windowInfo.rcWindow.right) {
226: hParent_X = 0;
227: }
228: if (hParent_Y >= (unsigned)windowInfo.rcWindow.bottom) {
229: hParent_Y = 0;
230: }
231: }
nengowin.cpp
233: /**
234: * パラメータの保存
235: * @param なし
236: * @return なし
237: */
238: void saveParameter(void) {
239: #ifndef CMDAPP
240: // アプリケーション・ウィンドウの位置取得
241: WINDOWINFO windowInfo;
242: windowInfo.cbSize = sizeof(WINDOWINFO);
243: GetWindowInfo(hParent, &windowInfo);
244: hParent_X = (unsigned)windowInfo.rcWindow.left;
245: hParent_Y = (unsigned)windowInfo.rcWindow.top;
246: if (hParent_X >= (unsigned)windowInfo.rcWindow.right) {
247: hParent_X = 0;
248: }
249: if (hParent_Y >= (unsigned)windowInfo.rcWindow.bottom) {
250: hParent_Y = 0;
251: }
252: #endif
253:
254: // XMLファイルへ書き込む
255: ptree pt;
256: ptree& child1 = pt.add("parameter.param", Option);
257: child1.add("<xmlattr>.type", "option");
258: ptree& child2 = pt.add("parameter.param", hParent_X);
259: child2.add("<xmlattr>.type", "wx");
260: ptree& child3 = pt.add("parameter.param", hParent_Y);
261: child3.add("<xmlattr>.type", "wy");
262:
263: const int indent = 4;
264: write_xml(getMyPath(APPNAME) + APPNAME + ".xml", pt, std::locale(),
265: xml_writer_make_settings<std::string>(' ', indent));
266: }
nengowin.cpp
268: /**
269: * パラメータの削除
270: * @param なし
271: * @return なし
272: */
273: void delParameter(void) {
274: remove((getMyPath(APPNAME) + APPNAME + ".xml").c_str());
275:
276: // 初期値設定
277: initParameter();
278: }
保存場所は、getMyPath 関数によって、ユーザーのAppDataの下にアプリケーションフォルダを作って保存する。
この処理は、GUI/CUI共通だ。
CUI用メインプログラム
nengowin.cpp
847: /**
848: * CUI用メインプログラム
849: * @param int argc
850: * @paramm char* argv[]
851: * @return int リターンコード
852: */
853: int main(int argc, char* argv[]) {
854: // パラメータ読み込み
855: loadParameter();
856: string sour = "";
857:
858: // pahooNormalizeTextオブジェクト
859: pNT = new pahooNormalizeText();
860: // 西暦⇔元号変換表読み込み
861: pNT->readTableEra(TABLEERA_FNAME);
862:
863: // コマンドライン・オプションの定義
864: options_description options("コマンドライン・オプション");
865: options.add_options()
866: ("option,o", value<std::string>()->default_value(Option), "変換オプション")
867: ("sour,s", value<std::string>(), "入力テキスト")
868: ("clip,c", "クリップボードから入力")
869: ("dest,d", value<std::string>(), "出力テキスト")
870: ("paste,p", "クリップボードへ出力")
871: ("help,h", "ヘルプ")
872: ("version,v", "バージョン情報")
873: ;
874: // コマンドライン・オプションの取得
875: variables_map vm;
876: try {
877: store(parse_command_line(argc, argv, options), vm);
878: } catch(const boost::program_options::error_with_option_name& e) {
879: ErrorMessage = e.what();
880: cerr << ErrorMessage << endl;
881: return 1;
882: }
883: notify(vm);
884:
885: // 変換オプション
886: auto opt = vm["option"].as<string>();
887:
888: wstring wdest = L"";
889: // ヘルプ情報
890: if (vm.count("help")) {
891: wdest = _SW(Help);
892: // バージョン情報
893: } else if (vm.count("version")) {
894: wdest = _SW(Version);
895:
896: // 変換実行
897: } else {
898: // 入力ファイル
899: wstring wsour = _SW(DEF_SOUR);
900: if (vm.count("sour")) {
901: auto infile = vm["sour"].as<string>();
902: ifstream ifs(infile.c_str());
903: if (ifs.fail()) {
904: ErrorMessage = infile + " が見つかりません";
905: cerr << ErrorMessage << endl;
906: return 1;
907: }
908: string sour = "";
909: string ss;
910: while (getline(ifs, ss)) {
911: sour += ss + "\r";
912: }
913: wsour = _SW(sour);
914: // クリップボードから
915: } else if (vm.count("clip")) {
916: char *ss = getClipboardData();
917: sour = ss;
918: wsour = _SW(sour);
919: // 標準入力から
920: } else {
921: string sour;
922: cin >> sour;
923: if (sour.length() > 0) {
924: wsour = _SW(sour);
925: }
926: }
927: wdest = pNT->convNengo(wsour, opt.c_str());
928: }
929:
930: // 出力ファイル
931: if (vm.count("dest")) {
932: // 改行コード置換
933: wregex re(_SW("\r"));
934: wdest = regex_replace(wdest, re, _SW("\n"));
935: auto outfile = vm["dest"].as<string>();
936: ofstream ofs(outfile.c_str());
937: ofs << _WS(wdest);
938: if (ofs.bad()) {
939: ErrorMessage = outfile + " への書き込みに失敗しました";
940: cerr << ErrorMessage << endl;
941: return 1;
942: }
943: ofs.close();
944: // クリップボードへ
945: } else if (vm.count("paste")) {
946: // 改行コード置換
947: wregex re(_SW("\r"));
948: wdest = regex_replace(wdest, re, _SW("\n"));
949: setClipboardData(_WS(wdest));
950: // 標準出力へ
951: } else {
952: // 改行コード置換
953: wregex re(_SW("\r"));
954: wdest = regex_replace(wdest, re, _SW("\n"));
955: cout << _WS(wdest);
956: }
957:
958: // オブジェクト解放
959: delete pNT;
960:
961: // パラメータ保存
962: Option = opt;
963: saveParameter();
964:
965: return 0;
966: }
GUI版の WinMain 関数はマルチスレッド対応で、すべてのスレッドが終わる前にコマンドラインに戻ってきてしまう。また、標準入出力のパイプ処理への対応も面倒であるため、CUI版は実行ファイルを分けることにした。
コマンドラインオプションの解釈は、Boost C++ライブラリ の options を利用した。
参考サイト
- PHPでテキスト中の和暦・西暦年号を統一する(その2):ぱふぅ家のホームページ
- 旧暦の計算:ぱふぅ家のホームページ
- WiX によるWindowsインストーラー作成:ぱふぅ家のホームページ
- C++ 開発環境の準備:ぱふぅ家のホームページ

また、コマンドライン版アプリケーションを同梱しており、バッチや他のプログラムから流し込んだテキストを変換することができる。
「PHPでテキスト中の和暦・西暦年号を統一する(その2)」で作ったPHPプログラムをC++に移植したものである。
(2026年3月15日)使用ライブラリ更新,optional名前衝突回避
(2025年11月23日)使用ライブラリ更新
(2025年8月2日)使用ライブラリ更新
(2025年3月29日)使用ライブラリ更新