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


ホームページに埋め込む場合に利用できるように、URLパラメータとして mode を指定することができる。gengo.php?mode=1 のようにして実行すると、概要表のみのHTML文を出力する。mode の値については、ソース・プログラム冒頭のコメントを参照されたい。
なお、TABLEタグに対してスタイルシートを指定しており、これはホームページ側で適宜設定してほしい。
サンプル・プログラム
gengo.php | サンプル・プログラム本体 |
pahooNormalizeText.php | テキスト正規化クラス pahooNormalizeText。 テキスト正規化クラスの使い方は「PHPで日本語テキストを正規化」を参照。include_path が通ったディレクトリに配置すること。 |
pahooCalendar.php | 暦計算クラス pahooCalendar。 暦計算クラスの使い方は「PHPで日出没・月出没・月齢・潮を計算」を参照。include_path が通ったディレクトリに配置すること。 |
解説:過去の元号を分析しやすいように配列に格納する
196: /**
197: * 過去の元号を計算しやすいように配列に格納する
198: * @param array $eras 元号を格納する配列
199: * @return int 格納した元号の数
200: */
201: function getEras(&$eras) {
202: //オブジェクト生成
203: $pnt = new pahooNormalizeText();
204: $pc = new pahooCalendar();
205:
206: $table = array($pnt->TABLE_AD_ERA2, $pnt->TABLE_AD_ERA1);
207: $cnt = 0;
208: foreach ($table as $tbl) {
209: foreach ($tbl as $key=>$val) {
210: $eras[$cnt]['name'] = $val;
211: if ($key == '99999999') $key = date('Ymd'); //現在元号
212: //開始年月日
213: preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})/', $key, $arr);
214: $eras[$cnt]['start_jd'] = $pc->AD2JD($arr[1], $arr[2], $arr[3], 0, 0, 0); //ユリウス日
215: list($year, $month, $day, $hour, $min, $sec) = $pc->JD2Gregorian($eras[$cnt]['start_jd']); //ユリウス日
216: $eras[$cnt]['start_year'] = (int)$year;
217: $eras[$cnt]['start_month'] = (int)$month;
218: $eras[$cnt]['start_day'] = (int)$day;
219: //終了年月日
220: if ($cnt > 0) {
221: $eras[$cnt - 1]['finish_jd'] = $eras[$cnt]['start_jd'] - 1;
222: $eras[$cnt - 1]['interval'] = $eras[$cnt - 1]['finish_jd'] - $eras[$cnt - 1]['start_jd'] + 1;
223: list($year, $month, $day, $hour, $min, $sec) = $pc->JD2Gregorian($eras[$cnt - 1]['finish_jd']); //ユリウス日
224: $eras[$cnt - 1]['finish_year'] = (int)$year;
225: $eras[$cnt - 1]['finish_month'] = (int)$month;
226: $eras[$cnt - 1]['finish_day'] = (int)$day;
227: }
228: $cnt++;
229: }
230: }
231: //オブジェクト解放
232: $pnt = NULL;
233: $pc = NULL;
234:
235: return $cnt;
236: }
配列の構造を以下に記す。2次元配列で、2次元目が連想配列になっている。
1次元 | 2次元 | 内 容 |
---|---|---|
番号 | name | 元号 |
start_year | 開始年(西暦) | |
start_month | 開始月 | |
start_day | 開始日 | |
start_jd | 開始ユリウス日 | |
finish_year | 終了年(西暦) | |
finish_month | 終了月 | |
finish_day | 終了日 | |
finish_jd | 終了ユリウス日 | |
interval | 期間(日) |
解説:有効な元号の数を数える
ユーザー関数 countEras は、これらを読み飛ばし、有効な元号の数を数える。
解説:元号に使われる漢字1文字を分析
251: /**
252: * 元号に使われる漢字1文字を分析
253: * @param array $eras 元号配列
254: * @param array $items 結果を格納する配列
255: * @return int 格納した漢字の数
256: */
257: function analyzeKanji($eras, &$items) {
258: $cnt = 0;
259: foreach ($eras as $era) {
260: if (mb_strlen($era['name']) == 0) continue;
261: //マルチバイト文字を1文字1文字に分解し配列に格納する
262: $arr = preg_split('//u', $era['name'], -1, PREG_SPLIT_NO_EMPTY);
263: foreach ($arr as $kanji) {
264: if (isset($items[$kanji])) {
265: $items[$kanji]['count']++; //出現回数
266: $items[$kanji]['interval'] += $era['interval']; //使用期間
267: } else {
268: $items[$kanji]['count'] = 1;
269: $items[$kanji]['interval'] = $era['interval'];
270: $cnt++;
271: }
272: }
273: }
274: return $cnt;
275: }

組み込み関数 preg_split を使って、マルチバイト文字を1文字1文字に分解する処理が肝である。
解説:分析結果を表示テキストにする:概要
277: /**
278: * 分析結果を表示テキストにする:概要
279: * @param array $eras 元号配列
280: * @param array $items 漢字の分析結果
281: * @return string 表示テキスト(HTML)
282: */
283: function resultSummary($eras, $items) {
284: $n = countEras($eras);
285: $k = count($items);
286:
287: $html =<<< EOT
288: <table class="plists">
289: <tr>
290: <td>元号の数</td>
291: <td style="text-align:right">{$n}</td>
292: </tr>
293: <tr>
294: <td>使われている漢字の数</td>
295: <td style="text-align:right">{$k}</td>
296: </tr>
297: </table>
298:
299: EOT;
300: return $html;
301: }
解説:分析結果を表示テキストにする:期間の長い元号
303: /**
304: * 分析結果を表示テキストにする:期間の長い元号
305: * @param array $eras 元号配列
306: * @param array $items 漢字の分析結果
307: * @return string 表示テキスト(HTML)
308: */
309: function resultIntervalLong($eras, $items) {
310: $ranking = RANKING;
311:
312: //並べ替え
313: uasort($eras, function($a, $b) {
314: if (isset($a['interval']) && isset($b['interval'])) {
315: $res = (int)($a['interval'] < $b['interval']);
316: } else {
317: $res = 0;
318: }
319: return $res;
320: });
321: $html =<<< EOT
322: <table class="plists">
323: <caption>期間の長い元号(トップ{$ranking})</caption>
324: <tr class="index">
325: <th>順位</th>
326: <th>元号</th>
327: <th colspan="2">期間</th>
328: </tr>
329:
330: EOT;
331: $i = 0;
332: $j = 1;
333: $order = $i;
334: $old = 0;
335: foreach ($eras as $era) {
336: if (mb_strlen($era['name']) == 0) continue;
337: if ($old != $era['interval']) {
338: $i += $j;
339: $j = 1;
340: $order = $i;
341: if ($i > $ranking) break;
342: } else {
343: $j++;
344: $order = '';
345: }
346: $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']);
347: $y = sprintf('%.1f年', $era['interval'] / 365.25);
348: $html .=<<< EOT
349: <tr>
350: <td class="order">{$order}</td>
351: <td class="kanji">{$era['name']}</td>
352: <td class="period">{$str}</td>
353: <td class="num">{$y}</td>
354: </tr>
355:
356: EOT;
357: $old = $era['interval'];
358: }
359: $html .=<<< EOT
360: </table>
361:
362: EOT;
363: return $html;
364: }

まず、組み込み関数 uasort を使って、配列を interval の大きい順に並び替える。
同数順位があり得ることから、順位に表示する値の計算を工夫した。
解説:分析結果を表示テキストにする:期間の短い元号
366: /**
367: * 分析結果を表示テキストにする:期間の短い元号
368: * @param array $eras 元号配列
369: * @param array $items 漢字の分析結果
370: * @return string 表示テキスト(HTML)
371: */
372: function resultIntervalShort($eras, $items) {
373: $ranking = RANKING;
374:
375: //並べ替え
376: uasort($eras, function($a, $b) {
377: if (isset($a['interval']) && isset($b['interval'])) {
378: $res = (int)($a['interval'] > $b['interval']);
379: } else {
380: $res = 0;
381: }
382: return $res;
383: });
384: $html =<<< EOT
385: <table class="plists">
386: <caption>期間の短い元号(トップ{$ranking})</caption>
387: <tr class="index">
388: <th>順位</th>
389: <th>元号</th>
390: <th colspan="2">期間(日)</th>
391: </tr>
392:
393: EOT;
394: $i = 0;
395: $j = 1;
396: $order = $i;
397: $old = 0;
398: foreach ($eras as $era) {
399: if (mb_strlen($era['name']) == 0) continue;
400: if ($old != $era['interval']) {
401: $i += $j;
402: $j = 1;
403: $order = $i;
404: if ($i > $ranking) break;
405: } else {
406: $j++;
407: $order = '';
408: }
409: $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']);
410: $y = sprintf('%d日', $era['interval']);
411: $html .=<<< EOT
412: <tr>
413: <td class="order">{$order}</td>
414: <td class="kanji">{$era['name']}</td>
415: <td class="period">{$str}</td>
416: <td class="num">{$y}</td>
417: </tr>
418:
419: EOT;
420: $old = $era['interval'];
421: }
422: $html .=<<< EOT
423: </table>
424:
425: EOT;
426: return $html;
427: }
解説:分析結果を表示テキストにする:出現頻度の多い漢字
429: /**
430: * 分析結果を表示テキストにする:出現頻度の多い漢字
431: * @param array $eras 元号配列
432: * @param array $items 漢字の分析結果
433: * @return string 表示テキスト(HTML)
434: */
435: function resultKanjiFrequency($eras, $items) {
436: $ranking = RANKING;
437:
438: //並べ替え
439: uasort($items, function($a, $b) {
440: return (int)($a['count'] < $b['count']);
441: });
442: $html =<<< EOT
443: <table class="plists">
444: <caption>出現頻度が多い漢字(トップ{$ranking})</caption>
445: <tr class="index">
446: <th>順位</th>
447: <th>漢字</th>
448: <th>出現回数</th>
449: </tr>
450:
451: EOT;
452: $i = 0;
453: $j = 1;
454: $order = $i;
455: $old = 0;
456: foreach ($items as $kanji=>$item) {
457: if ($old != $item['count']) {
458: $i += $j;
459: $j = 1;
460: $order = $i;
461: if ($i > $ranking) break;
462: } else {
463: $j++;
464: $order = '';
465: }
466: $html .=<<< EOT
467: <tr>
468: <td class="order">{$order}</td>
469: <td class="kanji">{$kanji}</td>
470: <td class="num">{$item['count']}</td>
471: </tr>
472:
473: EOT;
474: $old = $item['count'];
475: }
476: $html .=<<< EOT
477: </table>
478:
479: EOT;
480: return $html;
481: }
解説:分析結果を表示テキストにする:一度だけ使われた漢字
483: /**
484: * 分析結果を表示テキストにする:一度だけ使われた漢字
485: * @param array $eras 元号配列
486: * @param array $items 漢字の分析結果
487: * @return string 表示テキスト(HTML)
488: */
489: function resultKanjiOnce($eras, $items) {
490: $cols = 10; //表示列数
491:
492: $html =<<< EOT
493: <table class="plists">
494: <caption>一度だけ使われた漢字</caption>
495:
496: EOT;
497: $i = 0;
498: foreach ($items as $kanji=>$item) {
499: if ($item['count'] == 1) {
500: if ($i == 0) $html .= "<tr>\n";
501: $html .= "<td class=\"kanji\">{$kanji}</td>\n";
502: $i++;
503: if ($i >= $cols) {
504: $html .= "</tr>\n";
505: $i = 0;
506: }
507: }
508: }
509: while ($i < $cols) {
510: $html .= "<td class=\"kanji\"> </td>\n";
511: $i++;
512: if ($i >= $cols) $html .= "</tr>\n";
513: }
514:
515: $html .=<<< EOT
516: </table>
517:
518: EOT;
519: return $html;
520: }
解説:分析結果を表示テキストにする:使用期間の長い漢字
522: /**
523: * 分析結果を表示テキストにする:使用期間の長い漢字
524: * @param array $eras 元号配列
525: * @param array $items 漢字の分析結果
526: * @return string 表示テキスト(HTML)
527: */
528: function resultKanjiInterval($eras, $items) {
529: $ranking = RANKING;
530:
531: //並べ替え
532: uasort($items, function($a, $b) {
533: return (int)($a['interval'] < $b['interval']);
534: });
535: $html =<<< EOT
536: <table class="plists">
537: <caption>使用期間が長い漢字(トップ{$ranking})</caption>
538: <tr class="index">
539: <th>順位</th>
540: <th>漢字</th>
541: <th>のべ使用期間</th>
542: </tr>
543:
544: EOT;
545: $i = 0;
546: $j = 1;
547: $order = $i;
548: $old = 0;
549: foreach ($items as $kanji=>$item) {
550: if ($old != $item['interval']) {
551: $i += $j;
552: $j = 1;
553: $order = $i;
554: if ($i > $ranking) break;
555: } else {
556: $j++;
557: $order = '';
558: }
559: $y = sprintf('%.1f年', $item['interval'] / 365.25);
560: $html .=<<< EOT
561: <tr>
562: <td class="order">{$order}</td>
563: <td class="kanji">{$kanji}</td>
564: <td class="num">{$y}</td>
565: </tr>
566:
567: EOT;
568: $old = $item['interval'];
569: }
570: $html .=<<< EOT
571: </table>
572:
573: EOT;
574: return $html;
575: }
解説:元号を検索
577: /**
578: * 元号を検索
579: * @param string $query 検索キー(正規表現可能)
580: * @param array $eras 元号配列
581: * @return string 表示テキスト(HTML)
582: */
583: function searchKanji($query, $eras) {
584: //検索
585: $arr = array();
586: $cnt = 0;
587: foreach ($eras as $key=>$era) {
588: if (preg_match('/' . $query . '/u', $era['name']) > 0) {
589: $arr[$cnt] = $key;
590: $cnt++;
591: }
592: }
593:
594: //検索結果=ゼロ
595: if ($cnt == 0) {
596: $html = '<p>合致する元号はない.</p>';
597:
598: //検索結果
599: } else {
600: $html =<<< EOT
601: <table class="plists">
602: <caption>合致する元号</caption>
603: <tr class="index">
604: <th>番号</th>
605: <th>元号</th>
606: <th colspan="2">期間(日)</th>
607: </tr>
608:
609: EOT;
610: foreach ($arr as $key=>$val) {
611: $k = $key + 1;
612: $name = preg_replace('/(' . $query . ')/u', '<span class="match">$1</span>', $eras[$val]['name']);
613: $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']);
614: $html .=<<< EOT
615: <tr>
616: <td class="order">{$k}</td>
617: <td class="kanji">{$name}</td>
618: <td class="period">{$str}</td>
619: </tr>
620:
621: EOT;
622: }
623: $html .=<<< EOT
624: </table>
625:
626: EOT;
627: }
628: return $html;
629: }
参考サイト
- PHPでテキスト中の和暦・西暦年号を統一する(その2):ぱふぅ家のホームページ
巷間では新元号の予想が盛んだが、ちょうど「PHPでテキスト中の和暦・西暦年号を統一する(その2)」で紹介したクラス pahooNormalizeText の中に西暦⇔元号変換表があるので、これを利用し、PHPプログラムで過去の元号に関する分析を行ってみることにする。
南北朝時代については、南朝の元号を採用している。クラス pahooNormalizeText には北朝の元号がコメントアウトしてあるので、必要に応じて、南朝と切り替えてみてほしい。