re 正規表現

pythonを利用する上で、便利な表記などの個人的なメモです。基本的な部分は触れていません。対象も自分が便利だなと思ったものに限定されます。

正規表現は書くことたくさんありそうですが、思い出したり、新しい使い方に遭遇したりしたら随時更新します。

若い頃はいろいろな正規表現パターンを覚えようと思って頑張りましたが、今は必要な時だけ調べて利用するようにしています。全部覚えて使いこなす(正規表現マスター)になるのはしんどいですね。

github

  • githubのjupyter notebook形式のファイルはこちら

google colaboratory

  • google colaboratory で実行する場合はこちら

環境

!sw_vers
ProductName:	Mac OS X
ProductVersion:	10.14.6
BuildVersion:	18G2022
!python -V
Python 3.7.3

reの読み込み

正規表現は通常、解析したい文字列と解析の元となる正規表現のパターンの二つの文字列を必要とします。

読み込みは以下の通りです。

import re

re.compile()

正規表現による検索や置換をする方法は、二通りあります。一つが、re.complie()を用いて、正規表現パターンをあらかじめコンパイルしておいて、必要な時にそのオブジェクトを利用する方法。もう一つが、必要な時にコンパイルの処理を行い、それと同時にそのオブジェクトを利用する方法です。何度もその検索パターンを利用する場合は、re.compile()を利用した方が良いかと思います。

二つの利用方法の例を示します。

コンパイルを利用しない場合

obj = r'asdfghjkl'
target = r'gh'

ret = re.findall(target, obj)

if ret:
  print('findall の結果 : ', ret)
findall の結果 :  ['gh']

コンパイルを利用する場合

target = r'asdfghjkl'
pat = r'gh'

pat_target = re.compile(pat)

ret = pat_target.findall(pat)

if ret:
  print('findall の結果 : ', ret)
findall の結果 :  ['gh']

元となる正規表現のパターンにはrをつけると、バックスラッシュが必要な文字もそのまま表現できるので、デフォルトでつけておいた方が良いようです。

文字列の検索

検索関数は4つの方法があります。私個人としてはfindallを利用するのが最も多いです。

  • re.match : ターゲットの文字列の先頭が正規表現とマッチするか
  • re;.search : ターゲットの文字列が正規表現とマッチするか(先頭以外もOK)
  • re.findall : ターゲットの文字列で、正規表現とマッチする部分をリスト化して返す
  • re.finditer :ターゲットの文字列で、正規表現とマッチする部分をイテレータとして返す

re.match

import re

pat = r'[a-z_]+'
target = 'this_is_1_apple.'

result_match = re.match(pat, target)

print("### 文字列 ###")
print('pat    : ', pat)
print('target : ', target)
print()

print('### group ###')
if result_match:
  print('group :', result_match.group())
  print('span  :', result_match.span())
  print('start :', result_match.start())
  print('end   :', result_match.end())
else:
  print('matcn None')
### 文字列 ###
pat    :  [a-z_]+
target :  this_is_1_apple.

### group ###
group : this_is_
span  : (0, 8)
start : 0
end   : 8

re.search

pat = r'[0-9]+'
target = 'this_is_1_apple.'

result_search = re.search(pat, target)

print("### 文字列 ###")
print('pat    : ', pat)
print('target : ', target)
print()

print('### search ###')
if result_search:
  print('group :', result_search.group())
  print('span  :', result_search.span())
  print('start :', result_search.start())
  print('end   :', result_search.end())
else:
  print('search None')
### 文字列 ###
pat    :  [0-9]+
target :  this_is_1_apple.

### search ###
group : 1
span  : (8, 9)
start : 8
end   : 9
pat = r'(abc(...)*def)'
target = 'sssabcsabcssdefsssdefsssssssssssssssssssdefs'

result_search = re.search(pat, target)

print("### 文字列 ###")
print('pat    : ', pat)
print('target : ', target)
print()

print('### search ###')
if result_search:
  print('group  :', result_search.group())
  print('span   :', result_search.span())
  print('start  :', result_search.start())
  print('end    :', result_search.end())
  print('groups :', result_search.groups())
else:
  print('search None')
### 文字列 ###
pat    :  (abc(...)*def)
target :  sssabcsabcssdefsssdefsssssssssssssssssssdefs

### search ###
group  : abcsabcssdefsssdef
span   : (3, 21)
start  : 3
end    : 21
groups : ('abcsabcssdefsssdef', 'sss')

re.findall

pat = r'aaat(.*)tb([a-z]*)b'
target = 'aaatestbbbcccbbbbb'

result_findall = re.findall(pat, target)

print("### 文字列 ###")
print('pat    : ', pat)
print('target : ', target)
print()

print('### findall ###')
print(re.findall(pat, target))
### 文字列 ###
pat    :  aaat(.*)tb([a-z]*)b
target :  aaatestbbbcccbbbbb

### findall ###
[('es', 'bbcccbbbb')]

re.finditer

一致するイテレータを返します。

pat = r'aaat(.*)tb([a-z]*)b'
target = 'aaatestbbbcccbbbbb'

result_finditer = re.finditer(pat, target)

print("### 文字列 ###")
print('pat    : ', pat)
print('target : ', target)
print()

print('### finditer ###')
print(re.finditer(pat, target))
### 文字列 ###
pat    :  aaat(.*)tb([a-z]*)b
target :  aaatestbbbcccbbbbb

### finditer ###
<callable_iterator object at 0x107d76198>

文字列の置換

re.sub()

正規表現で文字列を置換します。個人的には最も良く利用します。

re.sub(正規表現パターン, 置換文字列, 置換対象文字列)
pat = r'0(8|9)0-[0-9]{4}-[0-9]{4}'
repl = '0X0-YYYY-ZZZZ'
obj = '080-1234-5678'

re.sub(pat, repl, obj)
'0X0-YYYY-ZZZZ'
pat = r'0(8|9)0-[0-9]{4}-[0-9]{4}'
obj = """
080-1234-5678
090-8765-4321
"""

print(re.sub(pat,r'0X0-YYYY-ZZZZ', obj))
0X0-YYYY-ZZZZ
0X0-YYYY-ZZZZ

後方参照

pat = r'0(8|9)0-([0-9]{4})-([0-9]{4})'
obj = '080-1234-5678'

re.sub(pat,r'0\g<1>0-\3-\2', obj)
'080-5678-1234'

数字が連続し、分離する場合。

pat = r'0(8|9)0-([0-9]{4})-([0-9]{4})'
obj = '080-1234-5678'

re.sub(pat,r'0\g<1>0-\3-\2', obj)
'080-5678-1234'

改行

pat = r'^0(8|9)0-[0-9]{4}-[0-9]{4}'
obj = """\
080-1234-5678
090-1234-4567
"""

re.sub(pat,'^0X0-YYYY-ZZZZ', obj, flags=re.MULTILINE)
'^0X0-YYYY-ZZZZ\n^0X0-YYYY-ZZZZ\n'