目次
サンプル・プログラムの実行例


ホームページに埋め込む場合に利用できるように、URLパラメータとして mode を指定することができる。gengo.php?mode=1 のようにして実行すると、概要表のみのHTML文を出力する。mode の値については、ソース・プログラム冒頭のコメントを参照されたい。
なお、TABLEタグに対してスタイルシートを指定しており、これはホームページ側で適宜設定してほしい。
サンプル・プログラム
gengo.php | サンプル・プログラム本体 |
pahooNormalizeText.php | テキスト正規化クラス pahooNormalizeText。 テキスト正規化クラスの使い方は「PHPで日本語テキストを正規化」を参照。include_path が通ったディレクトリに配置すること。 |
pahooCalendar.php | 暦計算クラス pahooCalendar。 暦計算クラスの使い方は「PHPで日出没・月出没・月齢・潮を計算」を参照。include_path が通ったディレクトリに配置すること。 |
解説:過去の元号を分析しやすいように配列に格納する
0196: /**
0197: * 過去の元号を計算しやすいように配列に格納する
0198: * @param array $eras元号を格納する配列
0199: * @return int格納した元号の数
0200: */
0201: function getEras(&$eras) {
0202: //オブジェクト生成
0203: $pnt = new pahooNormalizeText();
0204: $pc = new pahooCalendar();
0205:
0206: $table = array($pnt->TABLE_AD_ERA2, $pnt->TABLE_AD_ERA1);
0207: $cnt = 0;
0208: foreach ($table as $tbl) {
0209: foreach ($tbl as $key=>$val) {
0210: $eras[$cnt]['name'] = $val;
0211: if ($key == '99999999') $key = date('Ymd'); //現在元号
0212: //開始年月日
0213: preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})/', $key, $arr);
0214: $eras[$cnt]['start_jd'] = $pc->AD2JD($arr[1], $arr[2], $arr[3], 0, 0, 0); //ユリウス日
0215: list($year, $month, $day, $hour, $min, $sec) = $pc->JD2Gregorian($eras[$cnt]['start_jd']); //ユリウス日
0216: $eras[$cnt]['start_year'] = (int)$year;
0217: $eras[$cnt]['start_month'] = (int)$month;
0218: $eras[$cnt]['start_day'] = (int)$day;
0219: //終了年月日
0220: if ($cnt > 0) {
0221: $eras[$cnt - 1]['finish_jd'] = $eras[$cnt]['start_jd'] - 1;
0222: $eras[$cnt - 1]['interval'] = $eras[$cnt - 1]['finish_jd'] - $eras[$cnt - 1]['start_jd'] + 1;
0223: list($year, $month, $day, $hour, $min, $sec) = $pc->JD2Gregorian($eras[$cnt - 1]['finish_jd']); //ユリウス日
0224: $eras[$cnt - 1]['finish_year'] = (int)$year;
0225: $eras[$cnt - 1]['finish_month'] = (int)$month;
0226: $eras[$cnt - 1]['finish_day'] = (int)$day;
0227: }
0228: $cnt++;
0229: }
0230: }
0231: //オブジェクト解放
0232: $pnt = NULL;
0233: $pc = NULL;
0234:
0235: return $cnt;
0236: }
配列の構造を以下に記す。2次元配列で、2次元目が連想配列になっている。
1次元 | 2次元 | 内 容 |
---|---|---|
番号 | name | 元号 |
start_year | 開始年(西暦) | |
start_month | 開始月 | |
start_day | 開始日 | |
start_jd | 開始ユリウス日 | |
finish_year | 終了年(西暦) | |
finish_month | 終了月 | |
finish_day | 終了日 | |
finish_jd | 終了ユリウス日 | |
interval | 期間(日) |
解説:有効な元号の数を数える
ユーザー関数 countEras は、これらを読み飛ばし、有効な元号の数を数える。
解説:元号に使われる漢字1文字を分析
0251: /**
0252: * 元号に使われる漢字1文字を分析
0253: * @param array $eras 元号配列
0254: * @param array $items 結果を格納する配列
0255: * @return int格納した漢字の数
0256: */
0257: function analyzeKanji($eras, &$items) {
0258: $cnt = 0;
0259: foreach ($eras as $era) {
0260: if (mb_strlen($era['name']) == 0) continue;
0261: //マルチバイト文字を1文字1文字に分解し配列に格納する
0262: $arr = preg_split('//u', $era['name'], -1, PREG_SPLIT_NO_EMPTY);
0263: foreach ($arr as $kanji) {
0264: if (isset($items[$kanji])) {
0265: $items[$kanji]['count']++; //出現回数
0266: $items[$kanji]['interval'] += $era['interval']; //使用期間
0267: } else {
0268: $items[$kanji]['count'] = 1;
0269: $items[$kanji]['interval'] = $era['interval'];
0270: $cnt++;
0271: }
0272: }
0273: }
0274: return $cnt;
0275: }

組み込み関数 preg_split を使って、マルチバイト文字を1文字1文字に分解する処理が肝である。
解説:分析結果を表示テキストにする:概要
0277: /**
0278: * 分析結果を表示テキストにする:概要
0279: * @param array $eras 元号配列
0280: * @param array $items漢字の分析結果
0281: * @return string 表示テキスト(HTML)
0282: */
0283: function resultSummary($eras, $items) {
0284: $n = countEras($eras);
0285: $k = count($items);
0286:
0287: $html =<<< EOT
0288: <table class="plists">
0289: <tr>
0290: <td>元号の数</td>
0291: <td style="text-align:right">{$n}</td>
0292: </tr>
0293: <tr>
0294: <td>使われている漢字の数</td>
0295: <td style="text-align:right">{$k}</td>
0296: </tr>
0297: </table>
0298:
0299: EOT;
0300: return $html;
0301: }
解説:分析結果を表示テキストにする:期間の長い元号
0303: /**
0304: * 分析結果を表示テキストにする:期間の長い元号
0305: * @param array $eras 元号配列
0306: * @param array $items漢字の分析結果
0307: * @return string 表示テキスト(HTML)
0308: */
0309: function resultIntervalLong($eras, $items) {
0310: $ranking = RANKING;
0311:
0312: //並べ替え
0313: uasort($eras, function($a, $b) {
0314: if (isset($a['interval']) && isset($b['interval'])) {
0315: $res = (int)($a['interval'] < $b['interval']);
0316: } else {
0317: $res = 0;
0318: }
0319: return $res;
0320: });
0321: $html =<<< EOT
0322: <table class="plists">
0323: <caption>期間の長い元号(トップ{$ranking})</caption>
0324: <tr class="index">
0325: <th>順位</th>
0326: <th>元号</th>
0327: <th colspan="2">期間</th>
0328: </tr>
0329:
0330: EOT;
0331: $i = 0;
0332: $j = 1;
0333: $order = $i;
0334: $old = 0;
0335: foreach ($eras as $era) {
0336: if (mb_strlen($era['name']) == 0) continue;
0337: if ($old != $era['interval']) {
0338: $i += $j;
0339: $j = 1;
0340: $order = $i;
0341: if ($i > $ranking) break;
0342: } else {
0343: $j++;
0344: $order = '';
0345: }
0346: $str = sprintf('%d/%d/%d~%d/%d/%d', $era['start_year'], $era['start_month'], $era['start_day'], $era['finish_year'], $era['finish_month'], $era['finish_day']);
0347: $y = sprintf('%.1f年', $era['interval'] / 365.25);
0348: $html .=<<< EOT
0349: <tr>
0350: <td class="order">{$order}</td>
0351: <td class="kanji">{$era['name']}</td>
0352: <td class="period">{$str}</td>
0353: <td class="num">{$y}</td>
0354: </tr>
0355:
0356: EOT;
0357: $old = $era['interval'];
0358: }
0359: $html .=<<< EOT
0360: </table>
0361:
0362: EOT;
0363: return $html;
0364: }

まず、組み込み関数 uasort を使って、配列を interval の大きい順に並び替える。
同数順位があり得ることから、順位に表示する値の計算を工夫した。
解説:分析結果を表示テキストにする:期間の短い元号
0366: /**
0367: * 分析結果を表示テキストにする:期間の短い元号
0368: * @param array $eras 元号配列
0369: * @param array $items漢字の分析結果
0370: * @return string 表示テキスト(HTML)
0371: */
0372: function resultIntervalShort($eras, $items) {
0373: $ranking = RANKING;
0374:
0375: //並べ替え
0376: uasort($eras, function($a, $b) {
0377: if (isset($a['interval']) && isset($b['interval'])) {
0378: $res = (int)($a['interval'] > $b['interval']);
0379: } else {
0380: $res = 0;
0381: }
0382: return $res;
0383: });
0384: $html =<<< EOT
0385: <table class="plists">
0386: <caption>期間の短い元号(トップ{$ranking})</caption>
0387: <tr class="index">
0388: <th>順位</th>
0389: <th>元号</th>
0390: <th colspan="2">期間(日)</th>
0391: </tr>
0392:
0393: EOT;
0394: $i = 0;
0395: $j = 1;
0396: $order = $i;
0397: $old = 0;
0398: foreach ($eras as $era) {
0399: if (mb_strlen($era['name']) == 0) continue;
0400: if ($old != $era['interval']) {
0401: $i += $j;
0402: $j = 1;
0403: $order = $i;
0404: if ($i > $ranking) break;
0405: } else {
0406: $j++;
0407: $order = '';
0408: }
0409: $str = sprintf('%d/%d/%d~%d/%d/%d', $era['start_year'], $era['start_month'], $era['start_day'], $era['finish_year'], $era['finish_month'], $era['finish_day']);
0410: $y = sprintf('%d日', $era['interval']);
0411: $html .=<<< EOT
0412: <tr>
0413: <td class="order">{$order}</td>
0414: <td class="kanji">{$era['name']}</td>
0415: <td class="period">{$str}</td>
0416: <td class="num">{$y}</td>
0417: </tr>
0418:
0419: EOT;
0420: $old = $era['interval'];
0421: }
0422: $html .=<<< EOT
0423: </table>
0424:
0425: EOT;
0426: return $html;
0427: }
解説:分析結果を表示テキストにする:出現頻度の多い漢字
0429: /**
0430: * 分析結果を表示テキストにする:出現頻度の多い漢字
0431: * @param array $eras 元号配列
0432: * @param array $items漢字の分析結果
0433: * @return string 表示テキスト(HTML)
0434: */
0435: function resultKanjiFrequency($eras, $items) {
0436: $ranking = RANKING;
0437:
0438: //並べ替え
0439: uasort($items, function($a, $b) {
0440: return (int)($a['count'] < $b['count']);
0441: });
0442: $html =<<< EOT
0443: <table class="plists">
0444: <caption>出現頻度が多い漢字(トップ{$ranking})</caption>
0445: <tr class="index">
0446: <th>順位</th>
0447: <th>漢字</th>
0448: <th>出現回数</th>
0449: </tr>
0450:
0451: EOT;
0452: $i = 0;
0453: $j = 1;
0454: $order = $i;
0455: $old = 0;
0456: foreach ($items as $kanji=>$item) {
0457: if ($old != $item['count']) {
0458: $i += $j;
0459: $j = 1;
0460: $order = $i;
0461: if ($i > $ranking) break;
0462: } else {
0463: $j++;
0464: $order = '';
0465: }
0466: $html .=<<< EOT
0467: <tr>
0468: <td class="order">{$order}</td>
0469: <td class="kanji">{$kanji}</td>
0470: <td class="num">{$item['count']}</td>
0471: </tr>
0472:
0473: EOT;
0474: $old = $item['count'];
0475: }
0476: $html .=<<< EOT
0477: </table>
0478:
0479: EOT;
0480: return $html;
0481: }
解説:分析結果を表示テキストにする:一度だけ使われた漢字
0483: /**
0484: * 分析結果を表示テキストにする:一度だけ使われた漢字
0485: * @param array $eras 元号配列
0486: * @param array $items漢字の分析結果
0487: * @return string 表示テキスト(HTML)
0488: */
0489: function resultKanjiOnce($eras, $items) {
0490: $cols = 10; //表示列数
0491:
0492: $html =<<< EOT
0493: <table class="plists">
0494: <caption>一度だけ使われた漢字</caption>
0495:
0496: EOT;
0497: $i = 0;
0498: foreach ($items as $kanji=>$item) {
0499: if ($item['count'] == 1) {
0500: if ($i == 0) $html .= "<tr>\n";
0501: $html .= "<td class=\"kanji\">{$kanji}</td>\n";
0502: $i++;
0503: if ($i >= $cols) {
0504: $html .= "</tr>\n";
0505: $i = 0;
0506: }
0507: }
0508: }
0509: while ($i < $cols) {
0510: $html .= "<td class=\"kanji\"> </td>\n";
0511: $i++;
0512: if ($i >= $cols) $html .= "</tr>\n";
0513: }
0514:
0515: $html .=<<< EOT
0516: </table>
0517:
0518: EOT;
0519: return $html;
0520: }
解説:分析結果を表示テキストにする:使用期間の長い漢字
0522: /**
0523: * 分析結果を表示テキストにする:使用期間の長い漢字
0524: * @param array $eras 元号配列
0525: * @param array $items漢字の分析結果
0526: * @return string 表示テキスト(HTML)
0527: */
0528: function resultKanjiInterval($eras, $items) {
0529: $ranking = RANKING;
0530:
0531: //並べ替え
0532: uasort($items, function($a, $b) {
0533: return (int)($a['interval'] < $b['interval']);
0534: });
0535: $html =<<< EOT
0536: <table class="plists">
0537: <caption>使用期間が長い漢字(トップ{$ranking})</caption>
0538: <tr class="index">
0539: <th>順位</th>
0540: <th>漢字</th>
0541: <th>のべ使用期間</th>
0542: </tr>
0543:
0544: EOT;
0545: $i = 0;
0546: $j = 1;
0547: $order = $i;
0548: $old = 0;
0549: foreach ($items as $kanji=>$item) {
0550: if ($old != $item['interval']) {
0551: $i += $j;
0552: $j = 1;
0553: $order = $i;
0554: if ($i > $ranking) break;
0555: } else {
0556: $j++;
0557: $order = '';
0558: }
0559: $y = sprintf('%.1f年', $item['interval'] / 365.25);
0560: $html .=<<< EOT
0561: <tr>
0562: <td class="order">{$order}</td>
0563: <td class="kanji">{$kanji}</td>
0564: <td class="num">{$y}</td>
0565: </tr>
0566:
0567: EOT;
0568: $old = $item['interval'];
0569: }
0570: $html .=<<< EOT
0571: </table>
0572:
0573: EOT;
0574: return $html;
0575: }
解説:元号を検索
0577: /**
0578: * 元号を検索
0579: * @param string $query検索キー(正規表現可能)
0580: * @param array $eras 元号配列
0581: * @return string 表示テキスト(HTML)
0582: */
0583: function searchKanji($query, $eras) {
0584: //検索
0585: $arr = array();
0586: $cnt = 0;
0587: foreach ($eras as $key=>$era) {
0588: if (preg_match('/' . $query . '/u', $era['name']) > 0) {
0589: $arr[$cnt] = $key;
0590: $cnt++;
0591: }
0592: }
0593:
0594: //検索結果=ゼロ
0595: if ($cnt == 0) {
0596: $html = '<p>合致する元号はない.</p>';
0597:
0598: //検索結果
0599: } else {
0600: $html =<<< EOT
0601: <table class="plists">
0602: <caption>合致する元号</caption>
0603: <tr class="index">
0604: <th>番号</th>
0605: <th>元号</th>
0606: <th colspan="2">期間(日)</th>
0607: </tr>
0608:
0609: EOT;
0610: foreach ($arr as $key=>$val) {
0611: $k = $key + 1;
0612: $name = preg_replace('/(' . $query . ')/u', '<span class="match">$1</span>', $eras[$val]['name']);
0613: $str = sprintf('%d/%d/%d~%d/%d/%d', $eras[$val]['start_year'], $eras[$val]['start_month'], $eras[$val]['start_day'], $eras[$val]['finish_year'], $eras[$val]['finish_month'], $eras[$val]['finish_day']);
0614: $html .=<<< EOT
0615: <tr>
0616: <td class="order">{$k}</td>
0617: <td class="kanji">{$name}</td>
0618: <td class="period">{$str}</td>
0619: </tr>
0620:
0621: EOT;
0622: }
0623: $html .=<<< EOT
0624: </table>
0625:
0626: EOT;
0627: }
0628: return $html;
0629: }
参考サイト
- PHPでテキスト中の和暦・西暦年号を統一する(その2):ぱふぅ家のホームページ
巷間では新元号の予想が盛んだが、ちょうど「PHPでテキスト中の和暦・西暦年号を統一する(その2)」で紹介したクラス pahooNormalizeText の中に西暦⇔元号変換表があるので、これを利用し、PHPプログラムで過去の元号に関する分析を行ってみることにする。
南北朝時代については、南朝の元号を採用している。クラス pahooNormalizeText には北朝の元号がコメントアウトしてあるので、必要に応じて、南朝と切り替えてみてほしい。
(2021年1月29日)現在元号(令和)の完了日を当日までに変更
(2021年1月16日)PHP8対応