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 多くの文字列を柔軟に評価するときなどには使えますかねー.