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

すこしふしぎ.

VR/HI系院生による技術ブログ.まったりいきましょ.(友人ズとブログリレー中.さぼったら焼肉おごらなきゃいけない)

pythonで正規表現を使ってみる

こんばんは.1000chです. Check iOのようなコードパズルをやっていると.文字列を操作するシーンがとても多く出てきます. pythonの文字列オブジェクト自体にfindメソッドは存在しますが,そのままだとちょっち柔軟性に欠けますね.

ということで,正規表現を味方につけ,より柔軟な文字列操作を目指します.

正規表現

正規表現は,文字列を用いて”文字列の集合”を表現する手法です. "メタ文字"を用いた記法で,同時に複数の文字列を表現することができます. ま,詳しくはwikipedia先生にでも聞いてください.

代表的なルールはこんな感じ.

正規表現パターン 意味 ヒット例 ヒットしない例
. 任意一文字 c.t cat, cut cast
^ 先頭 ^get getName mygetName
$ 文字列末尾 foo$ foo, barfoo foobar
* 0回以上反復 ab* a,ab,abbb bb
+ 1回以上反復 ab+ ab,abbb a,bb
? 0or1回登場 ab? a,ab abb
{m} m回反復 a{3} aaa aa,aaaa
{m,n} m~n回反復 a{3,5} aaa,aaaa,aaaaa aa,aaaaaa
*?, +*, ??, {m,n}? non-greedyマッチ
最も小さくマッチする
- - -
[ ] 集合 [aiu] a,i,u e,o
[ - ] 集合:範囲指定 [a-z] a,i,u,w,z 1,!,$
[a-m0-9] a,g,m,0,5 !,$
[^] 集合:否定 [^a-z] 1,!,$ a,i,u,q,z
| 和集合 a|b a,b c,d
( ) グループ化
反復をグループに適用
(abc){2} abcabc abc
\ エスケープ
特殊文字直前に置く
- - -

表にするとそれなりの量がありますね. とりあえず基本はコレを覚えればokでしょう. 他にも(?....)から始まる様々な特殊パターンもあるようですが,なかなか複雑なので,上記に慣れてからにした方がよさそうです.

また,上記の正規表現に加えて特殊シーケンスも利用できるみたいです.

特殊シーケンス 意味 同義の正規表現
\number 既出グループ
\A 文字列の先頭 ^
\d 数字 [0-9]
\D 非数字 [^0-9]
\s 空白文字 [\n\t\r\f\v]
\S 非空白文字 [^\n\t\r\f\v]
\w 任意文字 [a-zA-Z0-9_]
\W 非任意文字 [^a-zA-Z0-9_]
\Z 文字列最後尾 $

このほかにも\bなどあるのですが,使い方がよくわかりませんw

pythonでの正規表現

python正規表現を利用するには,reモジュールを利用します. 基本的にはmatchメソッドかsearchメソッドを利用することで,正規表現にマッチした部分を扱うmatchオブジェクトを取得できます.

import re
m = re.search(expr, target_str)
m = re.match(expr, target_str)

という形が基本となるようです. expr正規表現を文字列として与え,走査したい文字列をtarget_strに与えます.

In [1]: import re

In [3]: m = re.search(r'h.g.','hoge huga')

In [4]: m.group() #マッチしたサブグループを返す
Out[4]: 'hoge' # 今回はサブグループが無いので,一番はじめにmatchしたものだけ

In [5]: re.findall(r'h.g.','hoge huga')
Out[5]: ['hoge', 'huga'] # matchするものすべてのリスト

また,もし同じ正規表現を何度も利用するようであれば,そのパターンをコンパイルして再利用することが推奨のようです.

prog = re.compile(expr)
result = prog.match(target_str)

複数パターンをちょいちょい使い回す場合にはこちらを利用すると効率的だとか. なお,re.search()re.match()を利用した時点で最後に利用された正規表現パターンコンパイルされたデータがキャッシュにのこるらしいです. なので,1パターンを使い回す場合にはわざわざコンパイルしないでよいとのこと.

便利な書き方

str型で言うところのstr,split()str.replace()正規表現を使って行います.

まずはsplitしてみましょう.

In [1]: text = "hoge huga bar piyo"

In [2]: text.split(' ')
Out[2]: ['hoge', 'huga', 'bar', 'piyo'] # str.split()

In [3]: import re

In [4]: re.split(' ',text)
Out[4]: ['hoge', 'huga', 'bar', 'piyo'] #普通のsplitと同じ挙動

In [6]: text = "hoge.huga_bar piyo:qux" #str.split()だと分割が難しい

In [7]: re.split('[._ :]',text)
Out[7]: ['hoge', 'huga', 'bar', 'piyo', 'qux']

次にreplaceです.re.sub(expr,repl,target)を使います.

In [1]: text = "my name is 1000ch"

In [2]: text.replace('1000ch','datch')
Out[2]: 'my name is datch'

In [4]: import re

In [5]: re.sub('1000ch','datch',text) #いわゆるreplace
Out[5]: 'my name is datch'

In [6]: text = "my_name,is.1000ch"

In [7]: re.sub("[_,.]"," ",text)
Out[7]: 'my name is 1000ch'

ちょっと例としては無理矢理な感がありますがw 多くの文字列を柔軟に評価するときなどには使えますかねー.

まとめ