目次
正規表現
参考サイト
- regex101
- 正規表現 HOWTO(Python公式)
- re — 正規表現操作(Python公式のreパッケージ説明)
- 言語処理100本ノック2015
- 正規表現サンプル(正規表現のお勉強)-HODADE SYSTEMS
- Microsoft 正規表現言語 リファレンス
- Microsoft Edge リファレンス
正規表現の記法は2種類
- 以下の「”regex”」箇所に
- 検索したい文字列の正規表現パターンを記載する(文字列だから””で囲む)
- p: pattern or regexObject
- r:raw文字列(エスケープシーケンスを回避)
- どちらの書き方でもよい
- 以下2つの書き方があるため、正規表現は最初とっつきにくい
パターン1:事前にコンパイル
- 正規表現を別でコンパイルしてから、検索するパターン
- =正規表現オブジェクトを作ってからそのメソッドを呼び出すケース
- =コンパイル済み正規表現のメソッドとして利用するケース、とも言う
- 同じ正規表現を複数回使うときやループの内側で使う場合は関数呼び出しを減らすため有効
- (以降の解説はパターン1の記法も多い)
- ループの外側で使うならパターン1,2どちらでもよいが、汎用性からこのパターン1を使う
import re
p = re.compile(r"regex")
match = p.search("string")
m.group()
# ex.
p = re.compile(r"P....n")
m = p.search("Hello Python")
m.group()
# >'Python'パターン2:関数の中でコンパイル
- モジュールレベルの関数、とも言う
- 正規表現オブジェクトを前もってコンパイルする必要がない(使える変数が減る?)
- (たぶんpositonが使えない?)
import re
m = re.search(r"regex", "string")
m.group()
m = re.search(r"P....n", "Hello Python")
m.group()
# >'Python'メタキャラクタ
- 一般の文字とは異なりその文字自身に特別な意味が割り当てられている文字のこと
| 記号 | 意味 | 例 | 合致例 |
| \d | 任意の十進数とマッチ [0-9]と同じ | \d\d\d \d{3} | 123% 4567 890abc |
| \D | 任意の非数字とマッチ [^0-9]と同じ | \D{3} | 890abc |
| \s | 任意の空白文字とマッチ [⎵\t\n\r\f\v]と同じ | abc⎵123 | abc⎵123 |
| \S | 任意の非空白文字とマッチ [^⎵\t\n\r\f\v]と同じ | abc⎵123 | abc⎵123 |
| \w | 任意の英数文字とマッチ [a-zA-Z0-9_]と同じ | abc⎵123 | abc⎵123 |
| \W | 任意の非英数文字とアンダーバーとマッチ [^a-zA-Z0-9_]と同じ | abc⎵123 | abc⎵123 |
| 記号 | 意味 | 例 | 合致する文字列 |
| * | 先行する文字、部分式、角括弧文字の 0個以上と合致 | a*b* | aaaaaa,aaabbbb,bbbbb |
| + | 〃 の 1個以上と合致 | a+b+ (long⎵)+ | aaaaaab,aaabbbb,abbbbb long⎵long⎵ago |
| ? | 〃の0個または1個以上と合致 {0,1}と同じ | AB?C Java⎵?SE | AB,ABC Java⎵SE, JavaSE |
| [] | 角括弧内の任意の文字(すなわちどれでもよい) | [A-Z]* | APPLE,CAPITAL,QWERY |
| () | まとめた部分式 (正規表現の「演算順序」で最初に評価される) | (a*b)* | aaabaab,abaaab,ababaaab |
| {m} | 直前の正規表現のm回の繰り返し | (Go!⎵){2} | Go! Go! Go! Go! Go! Go! Go! |
| {m,n} | 先行する文字、部分式、角括弧文字を m個からn個(両端含む)と合致 | a{2,3}b{2,3} | aabbb,aaabbb,aabb |
| [^] | 角括弧にない文字と合致 | [^A-Z] | apple,lowercase,qwerty |
| | | パイプで区切られた文字や部分式の どちらかに合致 | b(a|i|e)d | bad,bid,bed |
| . | 任意の1文字(記号、数字、空白等を含む)と合致(改行以外) | b.d | bad,bzd,b$d,b d |
| ^ | 文字または部分式が文字列の先頭にあることを示す 文字列内に改行文字があってもそこを含めマッチする | ^a ^This.* | apple,asdf,a This is a pen. |
| $ | 正規表現の末尾として使う これがないと全正規表現は「.*」を末尾に持ち 文字列の先頭部分と合致すること意味する | [A-Z]*[a-z]*$ line$ | ABCabc,zzzyx.Bob 1_line 2_line |
| \ | エスケープ文字 | \^\|\\ \. | ^|\ . |
| \b | 単語の境界 | 下部参照 | – |
| ?! | 「含まない」を意味する | ^((?![A-Z]).)*$ | no-caps-here,$ymb01s a4e f!ne |
| \n | 事前に( )で囲まれた部分正規表現に マッチした文字列 | ^(…)\1 | abcabcabc |
# \b 単語の境界
p = re.compile(r'book')
p.findall('book,notebook,bookstore')
# > ['book', 'book', 'book']
p = re.compile(r'\bbook\b')
p.findall('book,notebook,bookstore')
# > ['book']正規表現、引数の意味
- 正規表現とは
- 正しく規則性がある表現をした文字列、のこと
- つまりそのままでは単なる文字列
- 検索で使うには、コンパイルしてコンピューターに解釈してもらう必要がある
- コンパイルされた正規表現(regex)=正規表現オブジェクト(regexObject)
- 事前にコンパイルするか、関数内でコンパイルするかで
regexObjectまたはpatternが使われる場所が変わる
| キーワード・引数 | 意味 |
| regex | 正規表現(Regex Expression, RegExp等)←文字列で指定する エスケープシーケンスは以下を利用する ・raw文字列「r“**”」 ・pythonの「\」 |
| regexObject (pattern と記載されることが多い) | 正規表現オブジェクト パターンオブジェクト (⇔reモジュール) |
| string | 検索対象の文字列 |
| flags | 処理モード(コンパイルフラグ) |
| pos | 検索の開始位置 |
| endpos | 検索の終了位置 |
正規表現オブジェクトを生成する
p = re.compile(regex[, flags])
- 戻り値:regexObject(正規表現オブジェクト)
- 上記の記法はプリコンパイル(あらかじめコンパイルしておく方法)
- つまり、パターン1のやり方
- cf.もう一方の記法はモジュールレベル関数(
re.match("regex", "string"))
- reモジュールのメソッド
compileは、指定された文字列をコンパイルして、コンパイル済みの正規表現オブジェクトを生成する reモジュールはimport reで利用可- r“正規表現” ←raw文字列でエスケープシーケンスを無効にする
regex = re.compile(r'\d+')
# ↑本来はregexObjectと書くべきだが
p = re.compile(reflex)、でもよいコンパイルフラグ
- 参照
- パターン1、2いずれでも利用可
- 使用する際は先頭に
reモジュールが必要re.DOTALL、re.Sre.VERBOSE、re.X
| Flag(省略形) | 意味 |
| ASCII、A | \w, \b, \s, そして \d などをそれぞれのプロパティをもつ ASCII 文字だけにマッチさせます |
| DOTALL、S | . を改行を含む任意の文字にマッチするようにします。 |
| IGNORECASE、I | 大文字小文字を区別しないマッチを行います。 |
| LOCALE、L | ロケールに対応したマッチを行います。 |
| MULTILINE、M | ^ や $ の意味を変更し、複数行文字列に対するマッチングを行います。 |
| VERBOSE、X(=extend) | 冗長な正規表現を利用できるようにして、よりきれいで理解しやすくまとめることができます。 |
メソッド概要
| メソッド | 目的 |
| search( ) | 正規表現パターンがどこにマッチするかを調べる 戻り値:matchオブジェクト or None |
| match( ) | 文字列の先頭で正規表現パターンとマッチするかを調べる 戻り値:matchオブジェクト or None |
| findall( ) | 正規表現パターンにマッチする部分文字列をすべて探して「リスト」で返す 戻り値:リスト or 空リスト |
| finditer( ) | 正規表現パターンにマッチする部分文字列をすべて探して、イテレーターで返す matchオブジェクトを要素とするイテレーターのため、groupで取り出す必要あり 戻り値:イテレーター or ??? |
| sub( ) | 正規表現パターンにマッチする部分を置換する 戻り値:置換された後の文字列 |
| split( ) | 正規表現パターンにマッチする部分で分割する 戻り値:文字列をパターンで分割したリスト |
matchオブジェクト
- 上記メソッドでマッチした場合、matchオブジェクトが戻ってくる(findall( )は除く)
- matchオブジェクトは以下のメソッドで取り出す
| メソッド | 目的 |
| group( ) | 正規表現にマッチした文字列を返す m.group()またはm.group(0):マッチした全体を返す m.group(1):最初のマッチ文字列を返す |
| start( ) | マッチの開始位置を返す |
| end( ) | マッチの終了位置を返す |
| span( ) | マッチの位置(start, end)を含むタプルを返す |
regex = re.compile("(.+),(.+),(.+)") # 末尾にカンマは不要
match = regex.search("山田一郎,15,男性")
match.group() # or match.group(0)
# > '山田一郎,15,男性'
match.group(1)
# >'山田一郎'
match.group(2)
# >'15'
match.group(3)
# >'男性'
match.span()
# > (0,10)メソッドの使い方
search
文字列中のいずれかの場所でマッチ
- regexOjbect.search(string[, pos[, endpos])
- 戻り値:マッチ成功時は matchオブジェクト、失敗時は None
import re
regex = re.compile(r'\d+')
# マッチありの場合
m = regex.search('abc123')
m.group()
# > '123'
# マッチなしの場合
m = regex.search('abc')
m.group()
# > AttributeError: 'NoneType' object has no attribute 'group'
print(m)
# > 'None'match
文字列の先頭でマッチ
- regexOjbect.match(string[, pos[, endpos])
- 戻り値:マッチ成功時は matchオブジェクト、失敗時は None
import re
regex = re.compile(r'\d+')
m1 = regex.match('123abc')
m1.group()
# > '123'
m2 = regex.match('abc123')
m2.group()
# > AttributeError: 'NoneType' object has no attribute 'group'- 実際のプログラムでは以下のような表現をすればよい
impor re
p = re.compile("[a-z]+")
m = p.match("abあc")
if m:
print('Match found: ', m.group())
else:
print('No match')
# > Match found: abfullmatch
文字列全体にマッチ
- regexOjbect.fullmatch(string[, pos[, endpos])
- 戻り値:マッチ成功時は matchオブジェクト、失敗時は None
import re
regex = re.compile('\d+')
m1 = regex.fullmatch('123')
m1.group()
# > 123
m2 = regex.fullmatch('abc123')
m2.group()
# > AttributeError: 'NoneType' object has no attribute 'group'
findall
マッチする文字列を複数さがす1
- regexOjbect.findall(string[, pos[, endpos])
- 戻り値:マッチした全ての部分文字列を格納したリスト
- 戻り値はリストなので、group()で取り出そうとするとエラーになる
import re
regex = re.compile(r'\d+')
matches = regex.findall('123,456,789')
matches
# > ['123', '456', '789']
# マッチなしの場合は空の[]が返るimport re
regex = re.compile('\d+')
match = regex.findall('ab123cde456')
match
# > ['123', '456']import re
regex = re.compile('\d+')
m1 = regex.findall('ab123cde456')
m1 if m1 else print('No match')
# > ['123', '456']
m2 = regex.findall('abcde')
m2 if m2 else print('No match')
# > No matchfinditer
マッチする文字列を複数さがす2
- regexOjbect.finditer(string[, pos[, endpos])
- 戻り値:マッチした全ての部分文字列に対するmatchオブジェクトのiterrow
regex = re.compile(r'\d+')
matches = regex.finditer('123,456,789')
for m in matches:
print(f'{m.group()}:{m.start()}〜{m.end()}')
# 123:0〜3
# 456:4〜7
# 789:8〜11sub
マッチする文字列を置換する
- regexOjbect.sub(置換文字列,string[, 置換上限数])
p = re.compile('(blue|white|red)')
p.sub('color', 'blue shirts and white socks and red shoes')
# > 'color shirts and color socks and color shoes'p = re.compile('^hello')
p.sub('Hello', 'hello hello hello')
# > 'Hello hello hello'p = re.compile(r'(\d+):(\w+)')
p.sub('[\\2:\\1]', '1134:abc,56:def,789:ghi')
# > '[abc:1134],[def:56],[ghi:789]'split
マッチした全ての場所で分割する
- regexOjbect.split(string[, 置換上限数])
p = re.compile(r'\W+')
p.split('This is a test, short and sweet, of split().')
> # ['This', 'is', 'a', 'test', 'short', 'and', 'sweet', 'of', 'split', '']
p.split('This is a test, short and sweet, of split().', 3)
> # ['This', 'is', 'a', 'test, short and sweet, of split().']最短マッチを行うには
- 「*」「+」「?」といった繰り返しを表す特殊文字は、範囲が一番長くなるようにマッチする
- これを「欲張りマッチ」等という
- 「.+」は1文字以上の任意の長さの文字列のこと
- この場合、最初の「東京,」だけを取り出したくても取り出せない
regex = re.compile("^.+,")
match = regex.search("東京,大阪,名古屋,福岡")
match.group()
# 東京,大阪,名古屋, ←マッチした文字列がすべて取得される- それに対して「+」の代わりに「+?」を指定すると「最短マッチ」になる
- 同様に
- 「*」の代わりに「*?」を指定すると最短マッチになる
- 「?」の代わりに「??」を指定すると最短マッチになる
regex = re.compile("^.+?,")
match = regex.search("東京,大阪,名古屋,福岡")
match.group()
# 東京, ← マッチ文字列の最短が表示されるグルーピング
ポイント
( )で囲まれた正規表現をひとかたまりにするメリット
- 部分正規表現を定義できる
- 各種の量指定子(*や+など)をセットできる
- 例:
(abc)+
- 例:
- 演算子の優先順位を変更できる
- 例:
(Bill|William) Gates
- 例:
- 各種の量指定子(*や+など)をセットできる
- マッチした部分をキャプチャ(記憶)する
- 逆に()を付けないとキャプチャされない?
p = re.compile("(ab)*")
m = p.match("ababababab")
m.group()
# > 'ababababab'
p = re.compile("(a)b")
m = p.match("ababababab")
m.group()
# > 'ab'- サブグループは左から右へ1ずつナンバリングされている
- 0は全体を示す、1以降の番号は単に「(」の順番どおり
"(a(b)c)d"← 全体が0、「(」の1番目、2番目
p = re.compile("(a(b)c)d")
m = p.match('abcd')
m.group(0)
# > 'abcd'
m.group(1)
# > 'abc'
m.group(2)
# > 'b'
# グループ1から全てのグループの文字列をタプルで返す
m.groups()
# > ('abc', 'b')
# こんな表現もできる
m.group(2,1,2,1,1)
# > ('b', 'abc', 'b', 'abc', 'abc')import re
regex = re.compile(r'(\d{3})-(\d{4})')
str = "郵便番号は123-4567です"
match = regex.search(str)
match.group()
# > '123-4567'
match.group(1)
# > '123'
match.group(2)
# > '4567'こんな使い方もある
import re
regex = re.compile('(To|From):(.*)')
string = """
To:abc@gmail.com
From:xyz@yahoo.co.jp
"""
match = regex.search(string)
match.group()
# > 'To:abc@gmail.com'import re
regex = re.compile('(To|From):(.|\s)*')
string = """
To:abc@gmail.com
From:xyz@yahoo.co.jp
"""
match = regex.search(string)
match.group()
# > 'To:abc@gmail.com\nFrom:xyz@yahoo.co.jp\n'取り出さないグループ
(?:...)- グループ化された正規表現(部分正規表現)の一部を取り出したくない場合は「
(」の後ろに「?:」を付ける - 「…」は正規表現
import re
# 3つの(キャプチャ)グループがあるが
# 最初のグループ(「製品コード」)をキャプチャ対象外と設定する
regex = re.compile(r'(?:製品コード):([A-Z]{2})-(\d{2})')
str = "製品コード:AX-02"
match = regex.search(str)
match.group()
# > '製品コード:AX-02'
match.group(1)
# > 'AX'
match.group(2)
# > '02'regex = re.compile('^(...)(...)(...)')
str = 'abc123abc'
regex.findall(str)
# > [('abc', '123', 'abc')]
regex = re.compile('^(...)(?:...)(...)')
str = 'abc123abc'
regex.findall(str)
# > [('abc', 'abc')] ?タプル?名前付きグループ(未)
(?P<name>...)- 番号で参照する代わりに、グループに名前を付けて参照できる
- 番号参照はどれがどれかわかりにくいが、名前だとわかりやすい
p = re.compile(r'(?P<word>\b\w+\b)')
m = p.search('(((( Lots of punctuation )))')
m.group()
# > 'Lots'
m.group('word')
# > 'Lots'
m.group(1)
# > 'Lots'm = re.match(r'(?P<first>\w+) (?P<last>\w+)', 'Jane Doe')
m.groupdict()
# > {'first': 'Jane', 'last': 'Doe'}df[列]から正規表現で抽出(s.str.extract(r’〜’))
参考URL:
https://note.nkmk.me/python-pandas-str-extract-extractall/

r'(^.*?[都道府県])'
- ^:先頭、.*:任意文字、?[都道府県]:「都道府県」のどれか1文字
# 都道府県を抽出し、列に追加
df['都道府県'] = df['address'].str.extract(r'(^.*?[都道府県])', expand=True)
# 参考)都道府県に応じたコードを付与
df['都道府県コード'] = df['address']\
.str.extract(r'(^.*?[都道府県])')[0]\ # mapのためSeriesとして取り出す
.map({"神奈川県": "2", "東京都": "1", "千葉県": "3"})
