西暦を和暦に変換する方法
require 'date'
date = Date.new(2021, 4, 24)
NENGO_EIJI_KANJI = { 'M' => '明治', 'T' => '大正', 'S' => '昭和', 'H' => '平成', 'R' => '令和' }
nengo = date.jisx0301.sub(/\A(.)0*(\d+).+/){ NENGO_EIJI_KANJI[$1] + $2 }
puts nengo
先日、私の拙い記事に対して、コメントをいただき、西暦を和暦に変換する方法について教えていただいたので、学習した内容を整理するために記事にしてみました。
教えていただいたコードは上記のとおりで、Dateクラスとsubメソッドと正規表現を使用したものです。
subメソッド
subメソッドは、文字列の中に指定した文字列や正規表現でマッチした文字列が存在した場合に、指定した文字列で置換を行ってくれます。
返り値には置換後の文字列が返されます。
str = "HTML Ruby CSS"
puts str.(/CSS/, "JavaScript")
# HTML Ruby JavaScript
コードの可読性を上げるために、置換する文字列についてブロック形式で記載する方法もあります。
str = "HTML Ruby CSS"
puts str.(/CSS/){"JavaScript"}
# HTML Ruby JavaScript
正規表現
正規表現とは公式リファレンスによると、
正規表現は文字列のパターンを記述するための言語です。
また、この言語で記述されたパターンも正規表現と呼びます。
正規表現を用いると、文字列が指定したパターンを含んでいるかどうかを判定し、また含んでいるならばそれが文字列中のどの場所であるかを知ることができます。
と記載されています。
このリファレンスマニュアルをもとに、今回ご教示いただいた
nengo = date.jisx0301.sub(/\A(.)0*(\d+).+/){ NENGO_EIJI_KANJI[$1] + $2 }
を紐解いていきました。
間違っていたら、ご指摘願います。
まず、jisx0301メソッドで、西暦を和暦の英字表記に変換しています。
date.jisx0301
# R03.04.24
この「R03.04.24」に対して、subメソッドを使って、日本語表示の和暦に変換してあげます。
そのときに、正規表現にマッチした(/\A(.)0*(\d+).+/)
を{ NENGO_EIJI_KANJI[$1] + $2 }
に変換するようになっていますが、正規表現を使う場合は、//で囲ってやり、その中に正規表現を定義してあげます。
以下、\A(.)
と0*(\d+)
と.+
に分けて正規表現を解説していきます。
\A(.)
/A
は「文字列の先頭」を意味しており、今回の場合は「R」の部分に相当します。
()
はキャプチャする際に使われ、後ほど$1
と記載された特殊変数によって呼び出すことができるようにするために使われます。
.
は任意の1文字を意味しています。
ここまでをまとめると、\A(.)
で「先頭の文字列をキャプチャ化した」という意味になります。
0*(\d+)
*
は直前の文字を0回以上繰り返す意味をもち、0*
で「0が0回以上繰り返す」ことを意味しています。
()
は先程説明したキャプチャ化のためのもので、後に出てくる$2
でこの部分を呼び出すことができます。
\d
は[0-9]の整数を意味しており、+
は1回以上繰り返すことを意味しているので、\d+
で0~9の整数が1回以上繰り返されることを意味しています。
.+
最後に.+
についてですが、すでに説明した通り、.
任意の1文字、+
1文字以上繰り返されるということで、.+
で「何か文字があるもの全て該当する」という意味になります。
(/\A(.)0*(\d+).+/)の意味
以上のことをまとめると、先の
date.jisx0301
# R03.04.24
について、先頭の文字「R」をキャプチャ化して、年数である「03」の部分については「0」が入っている場合は、その部分はキャプチャ化せずに、つまりは、「03」、「02」等の場合は、「0」の部分は無視して、1以上の連続した整数についてはキャプチャ化しなさいということを(/\A(.)0*(\d+).+/)
は意味していることになります。
{ NENGO_EIJI_KANJI[$1] + $2 }の意味
$1
や$2
については、特殊変数と呼ばれ、パターンマッチしたときに、情報をセットすることができるものです。
前述のとおり、()
を使うことで、キャプチャ化することができ、最初にキャプチャ化したものを$1
で呼び出すことができます。
2番目にキャプチャ化したものは$2
で呼び出されるわけです。
以上を踏まえると、{ NENGO_EIJI_KANJI[$1] + $2 }
の意味は以下のように書き換えることができます。
{ NENGO_EIJI_KANJI["R"] + "03" }
という意味になり、
nengo = date.jisx0301.sub(/\A(.)0*(\d+).+/){ NENGO_EIJI_KANJI[$1] + $2 }
# (/\A(.)0*(\d+).+/) => "R03.04.24"
# { NENGO_EIJI_KANJI[$1] + $2 } => "令和3"
puts nengo
# "令和3"
に変換され、年号の部分のみ西暦を和暦に変換できるコードが完成しました。