読者です 読者をやめる 読者になる 読者になる

Pythonの正規表現でバックスラッシュを含む文字列とマッチする場合

Pythonのreモジュールでsearchなどの関数に正規表現を与える場合、rubyperlなどのように正規表現を直接与えることができず、文字列として与える必要があるため、WindowsのPath区切りにマッチさせたいときなどに正規表現内にバックスラッシュを使いたい場合、一つのバックスラッシュに対して"\\\\"とする必要がある。rプレフィックスを用いることで、文字列としてのエスケープは行われなくなるので、正規表現としての一つのバックスラッシュをr"\\"と書ける。 ただし文字列の最後にr"\"とは書けないことにも注意が必要。

reモジュールのsearch関数の第1引数は正規表現、第2引数はマッチ対象となる文字列だ。

>>> import re
>>> re.search("abc","abc")
<_sre.SRE_Match object; span=(0, 3), match='abc'>
>>> re.search(r"abc\\abc","abc\\abc")
<_sre.SRE_Match object; span=(0, 7), match='abc\\abc'>
>>> re.search("abc\\\\abc","abc\\abc")
<_sre.SRE_Match object; span=(0, 7), match='abc\\abc'>
>>> re.search(r"abc\\\\abc",r"abc\\abc")
<_sre.SRE_Match object; span=(0, 8), match='abc\\\\abc'>
>>> re.search(r"abc\\abc",r"abc\abc")
<_sre.SRE_Match object; span=(0, 7), match='abc\\abc'>
>>> re.search(r"abc\\abc\\",r"abc\abc\")
  File "<stdin>", line 1
    re.search(r"abc\\abc\\",r"abc\abc\")
                                       ^
SyntaxError: EOL while scanning string literal
>>> re.search(r"abc\\abc\\","abc\\abc\\")
<_sre.SRE_Match object; span=(0, 8), match='abc\\abc\\'>

以下のように正規表現の文字列として"\\"を与えてしまうと、文字列内でエスケープした結果として\正規表現として与えられてしまいエラーとなる。\は単独では正規表現として有効ではない。

>>> re.search("abc\\","abc\\")
Traceback (most recent call last):
  File "/usr/local/Cellar/python3/3.4.3_2/Frameworks/Python.framework/Versions/3.4/lib/python3.4/sre_parse.py", line 206, in __next
    c = self.string[self.index + 1]
IndexError: string index out of range
...

>>> re.search("\\","")
Traceback (most recent call last):
  File "/usr/local/Cellar/python3/3.4.3_2/Frameworks/Python.framework/Versions/3.4/lib/python3.4/sre_parse.py", line 206, in __next
    c = self.string[self.index + 1]
IndexError: string index out of range
...

バックスラッシュを含む文字列が変数に代入されており、それを正規表現として扱いたい場合、文字列中のバックスペースは二回エスケープされることになるので、予め"\\\\"とするか、あるいはrプレフィックスを与えてr"\\"としておく必要がある。

>>> a="abc\\\\abc"
>>> a
'abc\\\\abc'
>>> re.search(a,"abc\\abc")
<_sre.SRE_Match object; span=(0, 7), match='abc\\abc'>
>>> a=r"abc\\abc"
>>> a
'abc\\\\abc'
>>> re.search(a,"abc\\abc")
<_sre.SRE_Match object; span=(0, 7), match='abc\\abc'>

以下は誤ったやり方になる。上記の通り、rプレフィックスは文字列としてのエスケープを行わない、という指示になるので、新たにrプレフィックスを付けた文字列に変数をフォーマットしても期待通りには動かない。また、%rでフォーマットし直してもうまくいかない。どうにかして(正規表現として使われることを意識してしない)元の変数をsearchの第1引数に与えようと、replevalを試してみてもうまくいかない。

>>> a="abc\\abc"
>>> re.search(a,"abc\\abc")
>>> re.search(a,"abc\abc")
<_sre.SRE_Match object; span=(0, 6), match='abc\x07bc'>
>>> r"{0}".format(a)
'abc\\abc'
>>> "%r"%a
"'abc\\\\abc'"
>>> re.search("%r"%a,"abc\\abc")
>>>
>>>repr(a)
"'abc\\\\abc'"
>>> eval(repr(a))
'abc\\abc'

そのため、以下のようにsub関数による置換が必要になるかもしれない。

>>> a="abc\\abc"
>>> a=re.sub(r"\\",r"\\\\",a)
>>> a
'abc\\\\abc'
>>> re.search(a,"abc\\abc")
<_sre.SRE_Match object; span=(0, 7), match='abc\\abc'>