LoginSignup
33
30

More than 5 years have passed since last update.

長い正規表現を読みやすく管理する

Posted at

正規表現はとても便利なツールですが、長いものを書いていくと、とたんに読みづらくなってきます。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

…確実に読む気がしませんし、間違いがあっても多分気づきません。

33
30
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
30