正規表現ばかりに頼ってはいけない

f:id:anatoo:20140927151452p:plain:left:w180

文字列のパースをする必要がある時、どんな文字列にでも何でもかんでも正規表現で処理しようとするエンジニアをたまに見かける。

正規表現は確かに文字列を扱うための強力な手段だが、万能ではない。正規表現の性質上、そもそもパースできない文法があるからだ。従ってそういうケースの時には正規表現ではなく別の方法を使ったほうが良い。正規表現を無理やり使っても、バグを埋め込んだり、メンテナンスが難しかったり、正しく文字列をパース出来なかったりで良いことはあまりない。

正規表現がパースできない文字列

正規表現が苦手とする文法で一番よく言われるのは、再帰的な構文を含む文法である。例えば、括弧つきの数式なんかがそうで、1+1 でも (1+1) でも ( (1+1) ) でも ( ( (1+1) ) ) でも ( ( ( ( 1+1) ) ) ) でも、という風にいくらでも入れ子にできる。正規表現では、こういった文字列をパースしようにも括弧の対応を取ることができない(わからない人は実際試してみるといい)。

開発者の回りを見ると、括弧つきの数式以外にも再帰的な文法を持つ文字列はいくらでも転がっている。JSONだったりXMLだったりJavaScriptだったりS式だったり… あと正規表現の文法自体もサブパターンの括弧の対応を取れている必要があるので、正規表現は正規表現をパースできない。勿論、PCREの独自拡張で利用できる再帰的な文法のためのパターン(?R)があるけど、それはあくまで独自拡張である。

例え正規表現でパース可能な場合でも、正規表現が数十行から数百行に渡るような大規模な文法になれば、そんなものをメンテナンスしたいと思う人間はあまりいないだろう。

パーサコンビネータを使おう

無理やり正規表現を使う人がなぜ無理やり正規表現を使うかといえば、恐らくそれ以外のやり方を知らないからだろう。

それ以外のやり方というのは自分で一からパーサを書くのを含めていくつかあるけれども、自分はパーサコンビネータを使うことをお薦めする。

パーサコンビネータは、文字列をパースするための大変強力な手段である。正規表現よりも強力で、かつlex&yaccみたいな字句解析器と構文解析器を使うのよりも手軽だし、どの言語でもたいていパーサコンビネータのためのライブラリが存在する。

(次回へ続く)