LoginSignup
48

More than 5 years have passed since last update.

Python 3の正規表現で \d を使うな!

Posted at

案外知られていないようなのですが、Python 3 標準の reモジュールの正規表現では\dがいわゆる全角数字にもマッチします。

実際に試してみます。

>>> import re
>>> re.findall(r"\d", "012012")
['0', '1', '2', '0', '1', '2']
>>> 

\d'0''1''2'にもマッチしていますね。

この挙動があまり知られていない原因としては、

  • Python 3の公式ドキュメントの「正規表現 HOWTO」の説明が、かなり誤解を招きやすい書き方になっている。
  • \dがいわゆる全角数字にマッチするかどうかをわざわざテストする人は少ない
  • 敬虔なPythonistaは正規表現を使わないやり方を好む傾向がある

等の理由が考えられますが、他の原因もあるかもしれません。

\d[0-9]にだけマッチさせるには、\dの代わりに[0-9]を使うのがオススメだとreモジュールのドキュメントにも書いてあるのですが、他の言語用の長大な正規表現をそのまま流用したい等の理由でどうしても\dのままにしておきたい場合は、引数にflags=re.ASCIIを追加する、あるいは正規表現の先頭に(?a)を追加するという方法があります。

>>> import re
>>> re.findall(r"\d", "012012", flags=re.ASCII)
['0', '1', '2']
>>> re.findall(r"(?a)\d", "012012")
['0', '1', '2']
>>> 

ただし、これらのフラグは正規表現全体に影響します。詳細についてはreモジュールのドキュメントを読んでください。

なお、flags=は省略して、
re.findall(r"\d", "012012", re.ASCII)のように書くこともできますが、下手に省略するとハマることもありますので、省略しないことを強くオススメします。

ちなみに私自身は、

  • Python 2/3共用のコードは書かない
  • 標準の reモジュールではなく、regexモジュールを使いたい
  • 正規表現が長い時は先頭に(?a)等があった方が見通しが良い

等の理由から、

import regex

RE_DIGITS = regex.compile(r"""(?xa)
    \A\d+\Z""")


def is_digits(digits):
    if RE_DIGITS.match(digits) is not None:
        return True
    else:
        return False

のような書き方を好みますが、それでも\dを使うと不安になるので、なるべく[0-9]と書くようにしています。(is_digits()はあくまで例ですので念のため)

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
48