正規表現はとても便利なツールですが、長いものを書いていくと、とたんに読みづらくなってきます。Rubyでは、そんなときに対応するための手段がいくつかあります。
区切り文字の変更
これは以前に書きましたが、スラッシュを含む文字列を検索したい場合には%r@...@
のようにパーセントエンコードで書くと、\/
のようにエスケープする必要がなくなります。
複数行で書く
これまた詳しく説明してくださっている方がいらっしゃいますが、正規表現のオプションとしてx
を付けると、「改行やスペースを無視する」「#
から改行までをコメントとして扱う」ようになります。入れ子などの正規表現をインデントしつつ書けるようになって、見やすさも増します。
正規表現を式展開する
二重引用符の文字列リテラルと同様に、正規表現中でも変数展開が可能ですが、与えたものは#to_s
で展開されます。
そして、Regexp#to_s
では、「返される文字列は他の正規表現に埋め込んでもその意味が保持されるようになっています」。
ということで、これを使えば要素ごとに正規表現を区分して、それを組み立ててより大きな正規表現を作っていけます。
具体例
ここでは、「HTMLの属性値が0個以上続いた後に、タグを閉じる/>
あるいは>
が来る」、という正規表現を考えてみます(これを(?=)
で囲めば、「タグの中、属性の外」というアンカーとして使えます)。
まずは、HTML5の勧告に従って、要素文字列を定義しておきます。
# from HTML5 specification 2.4.1
SPACE_CHARACTER = /[ \t\r\n\f]/
# from HTML5 specification 8.1.2.3
ATTRIBUTE_NAME_REGEX = %r{(?>[^\x00'">/= \t\r\n\f]+)}
UNQUOTED_VALUE_REGEX = /(?>[ \t\r\n\f"'=<>`]+)/
QUOTED_VALUE_REGEX = /'.*?'|".*?"/m
あとは、これらを組み合わせて全体の正規表現としていきます。
IN_TAG_OUT_VALUE = %r{
(?:#{SPACE_CHARACTER}*+
#{ATTRIBUTE_NAME_REGEX}
(?:
#{SPACE_CHARACTER}*+
=#{SPACE_CHARACTER}*+
(?:#{UNQUOTED_VALUE_REGEX}|#{QUOTED_VALUE_REGEX})
)?
)*
#{SPACE_CHARACTER}*+/?>
}x
さて、この正規表現をバカ正直に書くとどうなるでしょうか。
/(?:[ \t\r\n\f]*+(?>[^\x00'">\/= \t\r\n\f]+(?:[ \t\r\n\f]*+=[ \t\r\n\f]*+(?:(?>[ \t\r\n\f"'=<>`]+)|'.*?'|".*?"))?)[ \t\r\n\f]}*+\/?>)/m
…確実に読む気がしませんし、間違いがあっても多分気づきません。