正規表現を使って、文字列から特定の文字より後ろを抽出するときには、抽出したいパターンをかっこ「()」でグルーピングしておくとよい。
# 正規表現を使って特定の文字(文字列)より後ろにある特定のパターンを抽出
import re
s = 'date: 2021/03/30, time: 05:30'
p = r'time: (.*)' # 「time: 」の後ろにある時間だけを抽出したい
# p = r'time: ([\d:]+)'
m = re.search(p, s)
print(m.group(1)) # 05:30
p = r'date: (.*),' # 「date: 」の後ろにある日付だけを抽出したい
# p = r'date: ([\d/]+)'
m = re.search(p, s)
print(m.group(1)) # 2021/03/30
p = r': ([\d/:]+)' # コロンの後ろにある日付と時刻を抽出したい
r = re.findall(p, s)
print(r) # ['2021/03/30', '05:30']
p = r': ([\d/]+).*: ([\d:]+)' # コロンの後ろにある日付と時刻を抽出したい
m = re.search(p, s)
print(m.groups()) # ('2021/03/30', '05:30')
p = r': (\d{4})/(\d{2})/(\d{2}).*(\d{2}):(\d{2})' # 数字をバラバラに抽出
m = re.search(p, s)
r = [int(m.group(x)) for x in range(1, 6)]
print(r) # [2021, 3, 30, 5, 30]
from datetime import datetime
d = datetime(*r)
print(d) # 2021-03-30 05:30:00
文字列中にあるコロンや半角空白文字など特定の文字あるいは文字列より後ろを抽出しようという場合、文字列の構造が簡単であれば、findメソッドとスライスを組み合わせたり、splitメソッドまたはpartitionメソッドを使ったりすることで、これを実現できる(その方法については「[解決!Python]文字列から特定の文字列以降や特定の文字列の前などを抽出するには」を参照のこと)。しかし、findメソッドとスライス、あるいはsplitメソッドで簡単に抽出できないときには正規表現を使う必要があるかもしれない。
例えば、変数sに「'date: 2021/03/30, time: 05:30'」という文字列が代入されているとしよう。時刻であれば、findメソッドあるいはrfindメソッドを使えば、正規表現を使わずともそれほど手間をかけることなく抽出できる。
s = 'date: 2021/03/30, time: 05:30'
target = 'time: ' # 「time: 」の後ろにある時間だけを抽出したい
idx = s.find(target)
r = s[idx+len(target):]
print(r) # 05:30
target = ': ' # 「: 」の後ろにある時間だけを抽出したい
idx = s.rfind(target)
r = s[idx+len(target):]
print(r) # 05:30
取り出す対象が文字列末尾にあるので、findメソッドで「time: 」が始まるインデックスを取得して、スライスを使えば簡単に抽出できるだろう(取得したインデックスに検索した文字列の長さを加算するのがポイントだ)。あるいは「: 」をrfindメソッドで右側から検索してもよい。
一方、正規表現を使うと次のように書ける。
import re
s = 'date: 2021/03/30, time: 05:30'
p = r'time: (.*)' # 「time: 」の後ろにある時間だけを抽出したい
# p = r'time: ([\d:]+)'
m = re.search(p, s)
print(m.group(1)) # 05:30
#print(m.group()) # time: 05:30
r = re.findall(p, s)
print(r) # ['05:30']
ポイントは、「time: 」より後ろにある抽出対象をかっこ「()」でグルーピングすることだ。これにより、re.searchメソッドを使ったときにはre.Matchオブジェクトのgroupメソッドにグループの番号を指定することで、re.findallメソッドを使ったときには文字列リストの要素として、抽出した部分にアクセスできる(re.findallメソッドは正規表現パターンにグループが含まれている場合は、グループにマッチした部分を要素とするリストを返送する)。
かっこで囲むグループの中は上のコードでは「(.*)」のように任意の長さの任意の文字となっているが、より限定するのなら、コメントにあるように「([\d:]+)」とすることも考えられる。時刻は数字とコロン「:」の組み合わせとなっているからだ(より厳密に時刻だけを抽出するのなら、さらに詳細な正規表現を記述すべきだが、ここでは割愛する)。
この文字列から今度は日付だけを抽出したいとする。findメソッドとスライスを使うと次のようになるだろう。
s = 'date: 2021/03/30, time: 05:30'
# 日付の前後のインデックスを得て、それらを指定してスライスする
target1 = 'date: '
idx1 = s.find(target1) + len(target1)
target2 = ','
idx2 = s.find(target2)
r = s[idx1:idx2]
print(r) # 2021/03/30
splitメソッドを使うなら次のようなコードになる。
# カンマ「,」で分割した前の要素を、半角空白文字で分割した後の要素が日付
r = s.split(',')[0].split(' ')[1]
print(r) # 2021/03/30
これに対して正規表現を使えば次のように書けるだろう。
p = r'date: (.*),' # 「date: 」の後ろにある日付だけを抽出したい
# p = r'date: ([\d/]+)'
m = re.search(p, s)
print(m.group(1)) # 2021/03/30
これも先ほどと同じで、「date: 」より後ろにある日付にマッチするパターンをかっこでグルーピングしている。ここでは「.*」として任意の文字を指定して、マッチが終わる条件としてカンマ「,」をパターンの最後に足しているが、コメントにあるように「r'date: ([\d/]+)'」というパターンでもよい(グループ内は数字か日付を区切るスラッシュ「/」の繰り返しであることを表すパターン)。
findメソッドとスライスを使う方法は、その意図はよく分かるが煩雑だ。splitメソッドを使う方法は簡潔だが、意味は分かりにくい。これに対して、正規表現は(グルーピングした日付のパターンが貧弱かもしれないが)意図もよく分かるし、コードも簡潔といえる。そのため、筆者としては正規表現を使う方法をオススメしたいところだ。
この文字列から日付と時刻を一度に抽出したいとなったら、正規表現を使うしかないだろう。以下に例を示す。
p = r': ([\d/:]+)' # コロンの後ろにある日付と時刻を抽出したい
r = re.findall(p, s)
print(r) # ['2021/03/30', '05:30']
この例では、「: 」の後に「数字か日付を区切るスラッシュか時刻を区切るコロン」が連続している部分にマッチするパターンを指定して、re.findallメソッドを呼び出している。これにより日付と時刻の両者が得られる。
re.searchメソッドを使うのであれば、次のような書き方もある。
p = r': ([\d/]+).*: ([\d:]+)'
m = re.search(p, s)
print(m.groups()) # ('2021/03/30', '05:30')
正規表現を使えば、日付と時刻を構成する数字だけを抽出し、それらを使ってdatetimeクラスのインスタンスを生成するといった処理も可能だ。以下に例を示す。
p = r': (\d{4})/(\d{2})/(\d{2}).*(\d{2}):(\d{2})' # 数字をバラバラに抽出
m = re.search(p, s)
r = [int(m.group(x)) for x in range(1, 6)]
print(r) # [2021, 3, 30, 5, 30]
from datetime import datetime
d = datetime(*r)
print(d) # 2021-03-30 05:30:00
Copyright© Digital Advantage Corp. All Rights Reserved.