atsukanrockのブログ

Microsoft系技術を中心にぼちぼち更新します

「全ての文字が○○である」正規表現の書き方

はじめに

ユーザ入力のチェックなどで、入力文字列の「全ての文字が○○である」ことを確かめるといった処理をすることがある。そういった処理は、正規表現を使って実装するのが手軽である。本エントリでは、その正規表現の書き方を述べる。

書き方

以下のような正規表現とし、それにマッチ「しない」ことを確かめる。

/[^<○○である文字>]/

サンプル

例えば、C#で「全ての文字がASCII printable characters*1である」ことを確かめるメソッドは、以下のように実装することになる。

public Boolean containsOnlyASCIIPrintableCharacters(String s)
{
    if (s == null)
    {
        throw new ArgumentNullException("s");
    }

    Regex regex = new Regex("[^\\u0020-\\u007E]");
    return !regex.IsMatch(s); // s が "" だと true を返すことに注意
}

なぜそう書くのか

本エントリではマッチ「しない」ものがあることを確かめることで、全てがマッチ「する」ことを確かめようとしているが、そうせずに全てがマッチ「する」正規表現を書くとすると、以下のようになるだろうか。

/^[<○○である文字>]*$/

この正規表現では^と$を使っているが、この$に注意しなければならない。なぜなら、$がマッチする位置が複雑なのだ。プログラミング言語によって異なるだろうし、正規表現のオプションによっても異なる。
例えば.NET Frameworkでは、MultiLineオプションをオンにした正規表現であれば'\n'の直前の位置、MultiLineオプションをオフにした正規表現であれば、文字列の末尾にマッチするようだ(詳細は「アトミック ゼロ幅アサーション」および「正規表現のオプション」を参照)。これに対しJavaPatternクラスでは、'\n'、"\r\n"、'\r'、'\u0085'、'\u2028'、'\u2029'を改行とみなす。
.NET FrameworkJavaの例からもわかるように、正規表現ではMultiLine(系の)オプションと改行の扱いに注意する必要がある。さらに言えば、プログラム中で正規表現を書くときにはできるだけ^や$を使いたくない。MultiLineオプションをオンにしなければ良いようにも思えるが、もしかすると、MultiLineオプションをオンにしなくても、$が文字列の末尾でなく改行の直前の位置にマッチする言語があるかもしれない。
つまり、本エントリで述べた正規表現の良さは、^や$を使わずに済んでいる点である。私が触れたことがある言語には限りがあるので、本エントリで述べた正規表現は、どの言語でも通用するものではないかもしれない。しかし、正規表現で「すべてが○○である」ことを確かめるには、「○○でないものがある」ことを確かめるのが良いというのは、おそらく全ての言語に通用するだろう*2

*1:ここでは、半角スペースを含むこととする

*2:というか、正規表現に限らずプログラミング全般で言えることだろう