More Related Content
More from Tomoya Kawanishi
More from Tomoya Kawanishi (20)
Ruby の正規表現について
- 2. 大手町.rb #17 「Ruby の正規表現について」
1自己紹介
Tomoya Kawanishi a.k.a. @cuzic
エネチェンジ株式会社 チーフエンジニア
電力会社、ガス会社を切り替えるなら、エネチェンジ経由で!
一般家庭も!法人も!
Ruby関西の中の人
2019年7月13日(土) 第87回Ruby関西勉強会
2019年9月15日(日) 大阪RubyKaigi 02
発表者として登壇くださる方、あとで声かけください。
大手町.rb の中の人
毎月 大手町.rb の開催を予定
東京駅、各線大手町駅から直結!
Ruby の初級者がメインターゲット
- 3. 大手町.rb #17 「Ruby の正規表現について」
今日のテーマ
Ruby の正規表現について
正規表現という用語について
英語の Regular Expression の日本語訳
言語学では「正則表現」
expression は「表現」以外に「式」という意味もある
違和感があるが、いまさらどうしようもない
正規表現はなぜ重要か
正規表現を使うと
特定の文字列にマッチするかどうかの判定ができる
文字列の一部を取得(capture)することがカンタンにできる
ログ処理、テキストバリデーションなどがカンタンにできる
特徴
ほぼすべての言語で実装されており、応用範囲が幅広い
非常にコンパクトに記述でき、可読性、メンテナンス性が高い
知る人ぞ知る機能も多いがベンリ
2
- 4. 大手町.rb #17 「Ruby の正規表現について」
正規表現の作り方
/hoge/ のように / で正規表現を作るのが一般的
中に / があるときは %r 記法を使う
正規表現にしたい文字列を引数で受け取るときなどは
Regexp.new を使うこともある
/ や %r の中では 式展開 #{} を使うこともできる
3
/hoge/ : もっとも一般的
%r(https://enechange¥.jp) # % 記法。
Regexp.new("https://enechange¥¥.jp") # Regexp.new
Regexp.compile("https://enechange¥¥.jp") # Regexp.compile
production_domain = "enechange¥¥.jp"
%r(https://#{production_domain})
- 5. 大手町.rb #17 「Ruby の正規表現について」
正規表現の例
正規表現を使うと、文字列マッチが簡単にできる
^ : アンカーの一つ。行頭にマッチする
() : 正規表現の一部をキャプチャするときに使う
. : メタ文字の1つ。改行を除く任意の1文字にマッチ
+ : メタ文字の1つ。直前の部分式を1回以上の繰り返し
にマッチ
4
url = "https://enechange.jp/try/input"
# / を使った正規表現リテラル。 マッチした position を返す。
url =~ /^https:¥/¥/enechange¥.jp/(.+)/ #=> 0
$1 #=> "try/input"
# %r を使った正規表現リテラル
url =~ %r(^https://enechange¥.jp/(.+))
# String#match? を使うと true false を返す
url.match?(%r(¥Ahttps://enechange¥.jp/(.+)))
- 6. 大手町.rb #17 「Ruby の正規表現について」
正規表現visualizer
Regulex という正規表現の visualizer がある。
慣れないうちは visualizer を使って、
その正規表現がどういう処理になるかを確認しながら、
進めると良い
5
# %r を使った正規表現リテラル
url =~ %r(^https://enechange¥.jp/(.+))
- 7. 大手町.rb #17 「Ruby の正規表現について」
正規表現を学ぶときのコツ
正規表現は仕様が膨大
すべてを最初から理解して使いこなす必要はない
業務で必要になる都度、少しずつ理解している範囲を広
げるといい
正規表現の練習用サイトもある
https://regexone.com/
https://www.hackerrank.com/domains/regex
正規表現マッチを簡単に確認できるサイトもある
http://refiddle.com/
https://rubular.com/
いろいろ既存サービスを使いこなして、上達を目指しま
しょう
6
- 8. 大手町.rb #17 「Ruby の正規表現について」
文字マッチ 7
. : 改行を除く任意の1文字
¥から始まらない文字 a など : a にマッチ
¥n : 改行にマッチ
¥記号 ¥/ ¥. など : その記号1文字にマッチ
¥w : 単語構成文字。 英小文字、英大文字、数字、アンダースコア、a-zA-Z0-9_
¥s : 空白文字。半角空白、タブ、改行、ほか [ ¥t¥r¥n¥f¥v]
¥d : 0から9 の数字
[abc] : 文字 a か b か c
[0-3] : 文字、 0 1 2 3 にマッチ
"2019-05-15".match?(/20¥d¥d-[01]¥d-[0-3]¥d/)
"example@example.jp".match?(/[-.¥w]+@)[-.¥w]+¥.jp/)
- 9. 大手町.rb #17 「Ruby の正規表現について」
アンカー 8
^ : 行頭にマッチ
$ : 行末にマッチ
¥A : 文字列の先頭にマッチする
¥Z : 文字列の末尾にマッチ。ただし、改行が最後にあるときはその直前にマッチ。
¥z : 文字列の末尾にマッチする。
¥b : 単語の境界にマッチする
body = File.read("/etc/passwd")
body =~ /root/ #=> 0
body =~ /nobody/ #=> 818
body =~ /¥Aroot/ #=> 0
body =~ /¥Anobody/ #=> nil
body =~ /bash$/ #=> 27
body =~ /bash¥Z/ #=> 2248
body =~ /bash¥z/ #=> nil
アンカーとは
幅0 の文字列にマッチするメタ文字列
- 10. 大手町.rb #17 「Ruby の正規表現について」
繰り返し、量指定子 9
* : 0回以上
+ : 1回以上
? : 0回もしくは1回
{n} :ちょうどn回(nは数字)
{n,} : n回以上(nは数字)
{,m} : m回以下(mは数字)
{n,m} : n回以上m回以下(n,mは数字)
直前の部分式を何回繰り返すかを指定する
欲張り(greedy)マッチ。最長の文字列にマッチする
"2019-05-15".match?(/¥d{4}-¥d¥d?-¥d¥d?/)
"2019-05-15".match?(/¥d{4}(-¥d{1,2}){2}/)
# /¥d{4}-¥d{1,2}-¥d{1,2}/ と同じ。
'<a href="#">'.match?(/¥<a¥s+href='?"?#'?"?¥s*¥>/)
m = '<div><a href="#">top</a></div>'.match(/<.+>/)
m[0] #=> "<div><a href=¥"#¥">top</a></div>"
m = /^.*(¥d+)¥./.match("Copyright 2013.")
m[1] #=> 3
- 11. 大手町.rb #17 「Ruby の正規表現について」
最小量指定子
直前の部分式を何回繰り返すかを指定する
最短の文字列にマッチする
10
*? : 0回以上
+? : 1回以上
?? : 0回もしくは1回
{n}? :ちょうどn回(nは数字)
{n,}? : n回以上(nは数字)
{,m}? : m回以下(mは数字)
{n,m}? : n回以上m回以下(n,mは数字)
m = '<div><a href="#">top</a></div>'.match(/¥<.+?¥>/)
m[0] #=> "<div>"
m = /^.*?(¥d+)¥./.match("Copyright 2013.")
m[1] #=> 2013
- 12. 大手町.rb #17 「Ruby の正規表現について」
キャプチャ
丸括弧()で、キャプチャできる
11
regex = %r(¥<a href="(.+?)".*?>(.+?)</a>)
m = '<div><a href="#">top</a></div>'.match(regex)
m[1] #=> "#"
m[2] #=> "top"
- 13. 大手町.rb #17 「Ruby の正規表現について」
いずれかへのマッチ
縦棒(パイプ) | で、複数の正規表現のどれかにマッ
チするかどうかという判定ができる
凝った正規表現を書くよりは、
| で分割する方が分かりやすいこともある
12
request.fullpath.match?(%r(¥A/(login|logout)))
# "/login" や "/logout" 、 "/login?from=blahblah" などにマッチする
message =~ /([0-9a-zA-Z]|[0-9a-zA-Z])+/
- 14. 大手町.rb #17 「Ruby の正規表現について」
正規表現関連のメソッド (String クラス) 13
# String#[regex] : 正規表現にマッチした文字列を返す
"We are hiring"[/¥b¥w+$/] #=> "hiring"
# String#[regex, index] : 正規表現にマッチし、その index のキャプチャ文字列を返す
"We are hiring"[/.+¥b(¥w+)$/, 1] #=> "hiring"
# String#=~ : 正規表現とマッチした position を返す
"We are hiring" =~ /¥b(¥w+)$/ #=> 7
# String#match : 正規表現とマッチした MatchData オブジェクトを返す
"We are hiring".match(/¥b(¥w+)$/) #=> #<MatchData "hiring" 1:"hiring">
body = File.read("/etc/passwd")
index = body =~ /bash$/
body.match(/^.+bash$/, index) # index 文字目からサーチを開始する
# String#match? : マッチ結果を true、 false で返す。動作が速い。
"We are hiring".match?(/¥b(¥w+)$/) #=> true
String クラスで正規表現関連で特に有用なメソッドを
5つ紹介します
- 15. 大手町.rb #17 「Ruby の正規表現について」
正規表現関連のメソッド (Regexp クラス) 14
# Regexp.quote : 正規表現をエスケープする
prod_url = "https://enechange.jp"
/¥b#{prod_url}¥b/ =~ "https://enechangeejp" #=> 0 マッチする
/¥b#{Regexp.quote(prod_url)}¥b/ =~ "https://enechangeejp" #=> nil。マッチしない。
# Regexp.union( pat1, pat2, …) : 引数のいずれかにマッチする正規表現を返す
# 複雑な正規表現を段階的に構築したいときに便利
re_letter = Regexp.union(/[0-9a-zA-Z]/, /[0-9A-Za-z]/)
message =~ /#{re_letter}+/
# Regexp#match、 Regexp#match? : String クラスの対応するメソッドと同じ
Regexp クラスで正規表現関連で特に有用なメソッドを
3つ紹介します
- 16. 大手町.rb #17 「Ruby の正規表現について」
正規表現関連のメソッド (MatchData クラス) 15
body = File.read("/etc/passwd")
m = body.match(/.+:(.+?sh)$/)
# MatchData#captures で括弧 () でキャプチャした文字列を配列で取得できる
m.captures #=> ["/bin/bash"]
# MatchData#[] でマッチした文字列を配列のようにアクセスできる
m[0] #=> "root:x:0:0:root:/root:/bin/bash"
m[1] = "/bin/bash"
m.pre_match #=> ""
m.post_match #=> "¥ndaemon:…(snip)"
# MatchData#begin(index) で index に対応する要素の開始位置が分かる
m.begin(0) #=> 0
m.begin(1) #=> 22
# MatchData#end(index) で index に対応する要素の終了位置が分かる
m.end(0) #=> 31
m.end(1) #=> 31
m2 = body.match(/.+:(.+?sh)$/, m.end(0))
m2.pre_match #=> "root:x…(snip)"
m2.begin(0) #=> 1615
- 17. 大手町.rb #17 「Ruby の正規表現について」
今回扱わなかったトピック
正規表現はとても奥が深く、今回は時間の関係もあり、
省略した話題が数多くあります。
興味がある方は自分で調べてください。
後方参照
特殊変数($`、$'、$1、$2 など)
名前付きキャプチャ
Unicodeプロパティ
アトミックグループ
部分式呼び出し(subexpression call)
先読み、後読み
条件分岐
非包含オペレータ
16
- 18. 大手町.rb #17 「Ruby の正規表現について」
まとめ
正規表現は文字列へのマッチ、ログ処理等でとても役立つ
正規表現を使う上で特に利用頻度が高いものを中心に
さまざまなノウハウを紹介
複雑な正規表現を作るときは
正規表現の visualizer ツールで、ロジックを確認したり
refiddle などでマッチするかどうかを確認したり
いきなり複雑な正規表現を作ろうとせず、段階的に構築しましょう
Regexp.escape
Regexp.union
正規表現の式展開 /#{regexp}/
17