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

サンプル・プログラムのダウンロード
nengo2.php | サンプル・プログラム本体 |
pahooNormalizeText.php | テキスト正規化クラス pahooNormalizeText。 テキスト正規化クラスの使い方は「PHPで日本語テキストを正規化」を参照ください。include_pathが通ったディレクトリに配置してください。 |
pahooInputData.php | データ入力に関わる関数群。 使い方は「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。 |
バージョン | 更新日 | 内容 |
---|---|---|
2.4.0 | 2023/09/03 | pahooInputDataクラス導入,他 |
2.3 | 2021/07/25 | PHP8対応,リファラ・チェック改良 |
2.21 | 2019/04/20 | bug-fix: seireki2wareki() |
2.2 | 2019/03/30 | リファラチェック機能追加 |
2.1 | 2017/12/03 | 2019年5月1日の改元に対応 |
バージョン | 更新日 | 内容 |
---|---|---|
4.0 | 2021/11/03 | getRuby_Yahoo():subwordを格納するようにした |
3.91 | 2021/10/07 | getRuby_Yahoo():デバッグコードを消去 |
3.9 | 2021/09/26 | getRuby_Yahoo():ルビ振り(V2)に変更 |
3.8 | 2020/07/11 | getRuby_Yahoo(): ローマ字も取得 |
3.71 | 2020/06/30 | http(): each()関数をforeachで代替:PHP7.2対応 |
バージョン | 更新日 | 内容 |
---|---|---|
1.5.0 | 2024/01/28 | exitIfExceedVersion() 追加 |
1.4.2 | 2024/01/28 | exitIfLessVersion() メッセージ修正 |
1.4.1 | 2023/09/30 | コメントの訂正 |
1.4.0 | 2023/09/09 | $_GET, $_POST参照をfilter_input()関数に置換 |
1.3.0 | 2023/07/11 | roundFloat() 追加 |
解説:準備
30: //データ入力に関わる関数群:include_pathに配置すること
31: require_once('pahooInputData.php');
32:
33: //PHPバージョン・チェック
34: exitIfLessVersion(MINUMUM_VERSION);
35:
36: //リファラチェック+リリースフラグの設定
37: if (isset($_SERVER['HTTP_HOST']) && ($_SERVER['HTTP_HOST'] == 'localhost')) {
38: define('FLAG_RELEASE', FALSE);
39: define('REFER_ON', '');
40: ini_set('display_errors', 1);
41: ini_set('error_reporting', E_ALL);
42: } else {
43: //リリース・フラグ(公開時にはTRUEにすること)
44: define('FLAG_RELEASE', TRUE);
45: //リファラ・チェック(直リン防止用;空文字ならチェックしない)
46: if (! isCommandLine()) {
47: define('REFER_ON', 'www.pahoo.org');
48: } else {
49: define('REFER_ON', '');
50: }
51: }
52:
53: //表示幅(ピクセル)
54: define('WIDTH', 600);
55:
56: //pahooNormalizeText クラス;各自の環境に合わせて変更すること
57: require_once('pahooNormalizeText.php');
58:
59: //例文
60: define('DEF_SOUR', "慶応四年一月三日、鳥羽・伏見の戦いが起きる。10月23日、明治天皇が即位。改元は1868年1月25日に遡って適用された。明治2年5月17日、五稜郭が開放され、戊辰戦争は終わる。");
61:
62: //変換関数の選択肢
63: $SelectFuncs = array(
64: //関数名 タイトル ラジオボタンcheked
65: 'seireki' => array('title'=>'西暦に統一', 'checked'=>''),
66: 'wareki' => array('title'=>'和暦に統一', 'checked'=>''),
67: 'mixture' => array('title'=>'西暦(和暦)', 'checked'=>'')
68: );
69:
70: //変換範囲の選択肢
71: $SelectMode = array(
72: //範囲 タイトル ラジオボタンcheked
73: 2 => array('title'=>'奈良時代まで', 'checked'=>''),
74: 1 => array('title'=>'近現代まで', 'checked'=>''),
75: 0 => array('title'=>'変換しない', 'checked'=>'')
76: );
77:
78: //変換範囲
79: $ModeEra = 2;
PHPのクラスについては、「PHPでクラスを使ってテキストの読みやすさを調べる」を参照のこと。

クラス・ファイルの読み込みは組み込み関数 require_once を用いて行う。パス名は絶対値で指定するか、事前に "php.ini" の include_path で指定されるパスにクラス・ファイルを配置する。
解説:西暦⇔元号変換テーブル
804: //元号の開始日の西暦yyyymmdd => 元号
805: //1581年以前はユリウス暦,1582年以降はグレゴリオ暦
806: var $TABLE_AD_ERA2 = array(
807: //飛鳥時代
808: '06450717' => '大化',
809: '06500322' => '白雉',
810: '06541124' => '', //空白期間
811: '06860814' => '朱鳥',
812: '06861001' => '', //空白期間
813: '07010503' => '大宝',
814: '07040616' => '慶雲',
815: '07080207' => '和銅',
816: //奈良時代
817: '07151003' => '霊亀',
818: '07171224' => '養老',
819: '07240303' => '神亀',
添え字は yyyymmdd の8桁で、対になる値(元号)が開始(改元)した西暦年月日(1581年以前はユリウス暦、1582年以降はグレゴリオ暦)を示す。Wikipediaの「元号一覧 (日本)」を参考にした。
元号が空文字になっている期間は元号が定められなかった部分で、元号変換の対象にはならない。

近現代とそれ以前とで変換範囲を分けられるようにするため、テーブルは2種類ある。
なお、南北朝時代については元号が重複するため、南朝の元号を採用した。北朝の元号はコメントアウトしてある。

変換範囲を格納する変数は $MODE_ERA である。
1062: '18610329' => '文久',
1063: '18640327' => '元治',
1064: '18650501' => '慶応',
1065: '18680125' => ''
1066: );
解説:文字列が元号かどうか判断
1099: /**
1100: * 文字列が元号かどうか判断する
1101: * @param string $str 文字列
1102: * @return bool TRUE/FALSE
1103: */
1104: function isera($str) {
1105: foreach ($this->TABLE_AD_ERA2 as $era) {
1106: if ($era != '') {
1107: $pat = '/' . $era . '/ui';
1108: if (preg_match($pat, $str) > 0) return TRUE;
1109: }
1110: }
1111: foreach ($this->TABLE_AD_ERA1 as $era) {
1112: if ($era != '') {
1113: $pat = '/' . $era . '/ui';
1114: if (preg_match($pat, $str) > 0) return TRUE;
1115: }
1116: }
1117: return FALSE;
1118: }
前述の、西暦⇔元号変換テーブルを総なめして判断している。
解説:西暦を元号に変換
1120: /**
1121: * 西暦を元号に変換する
1122: * @param string $prefix 年の前に付いている文字列
1123: * @param int $year 年
1124: * @param int $month 月(省略可能)
1125: * @param int $day 日(省略可能)
1126: * @return string 元号(+月日)
1127: */
1128: function ad2era($prefix, $year, $month=0, $day=0) {
1129: if ($prefix != $this->ESCYEAR) {
1130: $yyyymmdd = sprintf('%04d%02d%02d', $year, $month, $day);
1131: $dest = $prefix . $year . '年';
1132: $flag = FALSE;
1133:
1134: if (! $this->isera($prefix)) {
1135: if (!$flag && ($this->MODE_ERA >= 2)) {
1136: $yy = '';
1137: $last = '';
1138: foreach ($this->TABLE_AD_ERA2 as $start=>$era) {
1139: if ($yyyymmdd >= $start) {
1140: $yy = $year - substr($start, 0, 4);
1141: $last = $era;
1142: } else if ($last != '') {
1143: $str = ($yy == 0) ? '元' : (string)($yy + 1);
1144: $dest = $prefix . $last . $str . '年';
1145: $flag = TRUE;
1146: break;
1147: }
1148: }
1149: }
1150: if (!$flag && ($this->MODE_ERA >= 1)) {
1151: $yy = '';
1152: $last = '';
1153: foreach ($this->TABLE_AD_ERA1 as $start=>$era) {
1154: if ($yyyymmdd >= $start) {
1155: $yy = ($era == '') ? $year - 1 : $year - substr($start, 0, 4);
1156: $last = $era;
1157: } else if ($last != '') {
1158: $str = ($yy == 0) ? '元' : (string)($yy + 1);
1159: $dest = $prefix . $last . $str . '年';
1160: $flag = TRUE;
1161: break;
1162: }
1163: }
1164: }
1165: }
1166: } else {
1167: $dest = $year . '年';
1168: }
1169: if ($month > 0) $dest .= $month . '月';
1170: if ($day > 0) $dest .= $day . '日';
1171:
1172: return $dest;
1173: }
解説:元号を西暦に変換
1175: /**
1176: * 元号を西暦に変換する
1177: * @param string $prefix 年の前に付いている文字列(元号)
1178: * @param int $year 年
1179: * @param int $month 月(省略可能)
1180: * @param int $day 日(省略可能)
1181: * @return string 西暦(+月日)
1182: */
1183: function era2ad($prefix, $year, $month=0, $day=0) {
1184: if ($prefix != $this->ESCYEAR) {
1185: $dest = $prefix . $year . '年';
1186: if ($this->MODE_ERA >= 2) {
1187: foreach ($this->TABLE_AD_ERA2 as $start=>$era) {
1188: if (($era != '') && ($era == $prefix)) {
1189: $dest = $year + substr($start, 0, 4) - 1;
1190: $dest .= '年';
1191: break;
1192: }
1193: }
1194: }
1195: if ($this->MODE_ERA >= 1) {
1196: foreach ($this->TABLE_AD_ERA1 as $start=>$era) {
1197: if (($era != '') && ($era == $prefix)) {
1198: $dest = $year + substr($start, 0, 4) - 1;
1199: $dest .= '年';
1200: break;
1201: }
1202: }
1203: }
1204: } else {
1205: $dest = $year . '年';
1206: }
1207: if ($month > 0) $dest .= $month . '月';
1208: if ($day > 0) $dest .= $day . '日';
1209:
1210: return $dest;
1211: }
解説:西暦を和暦へ変換
244: /**
245: * 和暦に統一
246: * @param string $sour オリジナル・テキスト
247: * @return string 変換後テキスト
248: */
249: function wareki($sour) {
250: $pat = '/([^0-9〇一二三四五六七八九十百千万あ-ん、-〟!-¥]{0,4})\s*([0-9元〇一二三四五六七八九十百千万]+)年\s*([0-9〇一二三四五六七八九十]*)月?([0-9〇一二三四五六七八九十]*)日?/msui';
251:
252: return preg_replace_callback($pat, 'seireki2wareki', $sour);
253: }
正規表現を利用し、テキスト中の元号表記にパターンマッチさせる。
元号の属性は、数字・漢数字・平仮名は含まない4文字以下の文字列であることから、まず [(^0-9〇一二三四五六七八九十百千万あ-ん、-〟!-¥]{0,4}) によって元号部分にマッチさせる。
続く年は、算用数字または漢数字である。月・日も同様だが、この2つの記載は無くてもマッチするようにしている。

パターンマッチと同時に年号の置換処理を行うために、組み込み関数 preg_replace_callback を利用した。実際に置換を行うのはユーザー関数 seireki2wareki である。

ユーザー関数 seireki2wareki では、「PHPで漢数字を半角数字に変換する(整数版)」で作成した漢数字を数値に変換するユーザー関数 kan2num を呼び出して、漢数字を整数に変換しておく。
そして、前述の ad2era を使って元号に変換する。
解説:和暦を西暦へ変換
280: /**
281: * 西暦に統一
282: * @param string $sour オリジナル・テキスト
283: * @return string 変換後テキスト
284: */
285: function seireki($sour) {
286: $pat = '/([^0-9〇一二三四五六七八九十百千万あ-ん、-〟!-¥]{0,4})\s*([0-9〇元一二三四五六七八九十百千万]+)年\s*([0-9〇一二三四五六七八九十]*)月?([0-9〇一二三四五六七八九十]*)日?/msui';
287:
288: return preg_replace_callback($pat, 'wareki2seireki', $sour);
289: }
255: /**
256: * 和暦→西暦変換(漢数字対応)
257: * @param array $arr 和暦年月日(漢数字可能)
258: * @return string 西暦
259: */
260: function wareki2seireki($arr) {
261: global $ModeEra;
262:
263: $pnt = new pahooNormalizeText(); //pahooNormalizeTextクラス
264: $pnt->set_mode_era($ModeEra);
265:
266: $prefix = isset($arr[1]) ? $arr[1] : '';
267:
268: if (isset($arr[2])) {
269: if ($arr[2] == '元') $arr[2] = 1;
270: }
271: $year = isset($arr[2]) ? $pnt->kan2num($arr[2], 0) : 0;
272: $month = isset($arr[3]) ? $pnt->kan2num($arr[3], 0) : 0;
273: $day = isset($arr[4]) ? $pnt->kan2num($arr[4], 0) : 0;
274: $ad = $pnt->era2ad($prefix, $year, $month, $day);
275:
276: $pnt = NULL;
277: return $ad;
278: }
年月日を取り出すためのパターンは前述の wareki と同じである。

年号の置換処理を行うために、同様に preg_replace_callback を利用し、ユーザー関数 wareki2seireki を呼び出す。
wareki2seireki のロジックも、前述の seireki2wareki とほぼ同じである。
解説:西暦・和暦混合変換
305: /**
306: * 西暦(和暦)混合変換
307: * @param string $sour オリジナル・テキスト
308: * @param int $mode 変換範囲
309: * @return string 変換後テキスト
310: */
311: function mixture($sour) {
312: $sour = wareki($sour); //和暦に統一
313:
314: $pat = '/([^0-9〇一二三四五六七八九十百千万あ-ん、-〟!-¥]{0,4})([0-9元]+)年/ui';
315:
316: return preg_replace_callback($pat, 'seireki2mix', $sour);
317: }
291: /**
292: * 和暦→西暦(和暦)変換
293: * @param array $arr 元号,年
294: * @return string 西暦(和暦)
295: */
296: function seireki2mix($arr) {
297: $ad = wareki2seireki($arr);
298: $wareki = $arr[1] . $arr[2] . '年';
299:
300: return ($ad != $wareki) ? $ad . '(' . $wareki . ')' : $ad;
301:
302: return $ad . '(' . $arr[1] . $arr[2] . '年)';
303: }
まず、ユーザー関数 wareki を使って、入力テキスト中の年号を西暦に統一する。

年号の置換処理を行うために、同様に preg_replace_callback を利用し、ユーザー関数 seireki2mix を呼び出す。

ユーザー関数 seireki2mix では、前述の wareki2seireki を呼び出して西暦に変換する。
ここで、引数と変換結果が一致しなければ、西暦・和暦を結合した文字列を返す。一致していれば変換せず返す。
質疑応答
「テキスト中の和暦・西暦年号を変換(その2)」を興味深く拝見しました。【回答】
次のの文章を変換してみました。
「ペンの日:1935年11月26日、日本ペンクラブ創立。同クラブが1965年に制定」。
設定を「和暦に統一」もしくは、「西暦(和暦)」にすると、「ペンの日:」の「日:」が消えてしまいました。
月・日も変換の際の対象になっている高度なスクリプトのようで、その影響かとも思われますが、一応気が付きましたので報告します。
ご指摘のように、関数 wareki の置換表現に問題があることを確認しました。ありがとうございます。
他の置換表現も見直し、2017年12月2日にバージョンアップしました。ご確認下さい。
参考サイト
- C++ でテキスト中の和暦・西暦年号を統一する:ぱふぅ家のホームページ
- PHPでテキスト中の和暦・西暦年号を統一する:ぱふぅ家のホームページ
- PHPでテキスト中の和暦・西暦年号を統一(Windowsアプリ版):ぱふぅ家のホームページ
- 元号一覧 (日本):Wikipedia
今回は、飛鳥時代の最初の元号「大化」(645年)まで遡って変換できるようにし、さらに、改元の月日(たとえば平成なら1989年1月8日)までチェックして変換するPHPプログラムをつくることにする。
なお、「1000年後の世界」のように、年号なのかテキストなのか、パターンだけでは判別しにくいコンテクストがあるため、「\1000年後の世界」のように年号の前にエスケープ文字を付けることで変換を回避するようにした。
(2023年9月3日)pahooInputDataクラス導入,他