正規表現を実装するため、ミニアプリを作ります。
条件
・住所を入力させる(都道府県と市区町村のみ)
・都道府県を正規表現で取得
・市区町村を正規表現で取得
・それぞれ取得したものを配列にいれていく
・リスト表示させて、配列にいれこんだ、データを取り出せるメソッドを作成する
コーディングしていく
def enter_address(personal_informations)
puts "住所を入力してください"
address = gets.to_s
prefecture_address = address.scan(/.*[^都道府県][都道府県]/)
puts "都道府県名は#{prefecture_address}"
puts "---------------------------------------"
municipality_address = address.scan(/.+?郡.+?[町村]|.+?市.+?区|.+?[市区町村].*/)
municipality_address = address.gsub!(/.*[都道府県]/,'').chomp
puts "市区町村名は#{municipality_address}"
puts "---------------------------------------"
puts "電話番号を入力してください(携帯、市外局番両方可)"
number = gets.to_s
regular_number = number.scan(/0[1-9]\d{0,3}[-(]\d{1,4}[-)]\d{4}/)
puts "#{regular_number}"
puts "---------------------------------------"
personal_information = { prefecture_address: prefecture_address, municipality_address: municipality_address, regular_number: regular_number }
personal_informations << personal_information
end
def show(personal_informations)
puts "番号を入力してください"
index = 1
personal_informations.each do |personal_information|
puts "#{index}: #{personal_information[:prefecture_address]} #{personal_information[:municipality_address]} #{personal_information[:regular_number]}"
index += 1
end
input = gets.to_i
input -= 1
puts "都道府県は#{personal_informations[input][:prefecture_address]}です"
puts "市町村名は#{personal_informations[input][:municipality_address]}です"
puts "電話番号は#{personal_informations[input][:regular_number]}です"
end
personal_informations = []
while true do
puts "下記に該当する番号を選んでください"
puts "---------------------------------------"
puts "1:都道府県名と電話番号を入力"
puts "2:リストをみる"
puts "3:終了する"
puts "---------------------------------------"
case gets.to_i
when 1
enter_address(personal_informations)
when 2
show(personal_informations)
when 3
exit
else
puts "無効な値です"
puts "---------------------------------------"
end
end
動作確認
下記に該当する番号を選んでください
---------------------------------------
1:都道府県名と電話番号を入力
2:リストをみる
3:終了する
---------------------------------------
1
住所を入力してください
千葉県浦安市
都道府県名は["千葉県"]
---------------------------------------
市区町村名は浦安市
---------------------------------------
電話番号を入力してください(携帯、市外局番両方可)
02-2222-2222
["02-2222-2222"]
---------------------------------------
下記に該当する番号を選んでください
---------------------------------------
1:都道府県名と電話番号を入力
2:リストをみる
3:終了する
---------------------------------------
1
住所を入力してください
東京都練馬区
都道府県名は["東京都"]
---------------------------------------
市区町村名は練馬区
---------------------------------------
電話番号を入力してください(携帯、市外局番両方可)
090-3333-3333
["090-3333-3333"]
---------------------------------------
下記に該当する番号を選んでください
---------------------------------------
1:都道府県名と電話番号を入力
2:リストをみる
3:終了する
---------------------------------------
2
番号を入力してください
1: ["千葉県"] 浦安市 ["02-2222-2222"]
2: ["東京都"] 練馬区 ["090-3333-3333"]
苦労した点
・そもそも正規表現を一から考えるのってむずいなぁと感じました。
・市区町村を正規表現だけで取り出すのは断念しました・・・結果として二行つかいました。
municipality_address = address.scan(/.+?郡.+?[町村]|.+?市.+?区|.+?[市区町村].*/)
municipality_address = address.gsub!(/.*[都道府県]/,'').chomp
ここの部分二行になってるんですが、どうしても都道府県も出てきてしまうので、
無理やりですが、都道府県は変換して削除して出力するようにしました。
たぶんこれはナンセンスなやり方だと思いますが・・・・これも正規表現で取得できるはずなので、時間をかけて理解を深めていこうと思います。
追記2020/01/22 正規表現1行で出来ました。
municipality_address = address.scan(/[都道府県](.+?郡.+?[町村]|.+?市.+?区|.+?[市区町村].*)/)
こちらで1行で表現できました。ありがとうございました。
追記:正規表現のメソッドで「東京都府中市」と住所を入力した際に、東京都府と表示されてしまう問題
もともと
①
prefecture_address = address.scan(/.*[都道府県]/)
と記述されたものを下記のように
②
prefecture_address = address.scan(/.*[^都道府県][都道府県]/)
と記述することで解決しました。(上記に記述したコードも修正しました)
②を言語化すると、おそらく
→任意の文字を0回以上繰り返し、都道府県のいずれかの文字ではなく、最後に都道府県のいずれかの文字を抽出
っということでしょうか?
なんとなく理解できたような・・・・。
む、これだと京都府と入力した際に「京都」と出力されてしまう、これは・・・
地道に学習しなおします。。。。
そもそも住所を正規表現で取り出すのはナンセンス?
っという記事をよくみます。
バリデーションをかけたほうが手っ取り早そう。
正規表現を使用する場合、、、
↓
簡易的な抽出、データの加工に必要な場合に使用。
っと一旦心得ようと思いました。
追記:@scivola さんからアドバイスいただき、Test::Unitを使ってミニテストを実行いたしました。
少し時間かかっちゃいました・・・。
ちょっと無理やり感ありますが、テストを100%通すというところまでをやりました。
【参考サイト】
すごい参考になりました。
https://docs.ruby-lang.org/ja/2.0.0/library/test=2funit.html
伊藤先生(@jnchito)
https://qiita.com/jnchito/items/ff4f7a23addbd8dbc460
【準備】
必要?かどうかわからなかったんですが
gem install test-unit
いれました。
できたミニテストのコード
# 自動テストのためのライブラリーを読み込む
require "test/unit"
# 住所文字列から都道府県と市区町村を抽出する
def decompose_address(address)
# ここに素晴らしいコードを何か書く。
address = "東京都府中市"
prefecture = address.scan(/.*[^都道府県][都道府県]/)
prefecture_address = prefecture[0]
city = address.scan(/[都道府県](.+?郡.+?[町村]|.+?市.+?区|.+?[市区町村].*)/)
city_address = city[0][0]
return prefecture_address, city_address # 得られた都道府県と市区町村の文字列を返す
end
# ここから先がテストコード
class AddressTest < Test::Unit::TestCase
test "decompose" do
assert_equal ["東京都","府中市"], decompose_address("東京都府中市")
end
end
実行結果
Started
.
Finished in 0.000841 seconds.
--------------------------------------------------------------------------------------------------
1 tests, 1 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
--------------------------------------------------------------------------------------------------
1189.06 tests/s, 1189.06 assertions/s
かっこが邪魔でテストに通らなかったため、配列の中身を指定しました。
以外と、へっ!?ってなるところでした。正規表現の書き方次第で得られる結果も変わってくるのかと、良い経験になりました。
※メモ → assertions = 実行した検証数
ばりばり、@scivola さんのパクってます(・。・;
テストって複数種類あるんですね。
今回やったテストが本当に簡易的なテストをやりたいって場合に使われているようです。
テストをやる意味についても勉強になりました。
https://blog.jnito.com/entry/2019/07/07/102734
間違ってたらすいません。。。。
ここが違う、ここはこうしたほうがセンスが良い等々ございましたら
ご指摘いただけますと幸いです。