正規表現とは文字列をパターンで表現したものです。
主に以下の3つで使用します。
・パターンに適合している文字列があるかどうかを判定する
・パターンに適合している文字列があれば、それを別の文字列に置換する
・パターンに適合している文字列があれば、それを抜き出す
文字列メソッドでも、「あるかないか判定」や、「置換」はできますが、正規表現と何が違うのかと言いますと、文字列そのもので判断するのか、パターンで判断するのかの違いです。
文字列メソッドの復習
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
moji = "正規表現とは、文字列の集合を一つの文字列で表現する方法の一つである。" # findであるかないか判定する a = moji.find("正規表現") print(a) # インデックス番号0を返す # なければ-1を返すので「あるかないか」の判定はこのようにする if a > -1: print("あります") else: print("ありません") a = moji.find("世紀表現") print(a) # ないので-1を返す if a > -1: print("あります") else: print("ありません") # in演算子で「あるかないか」判定する a = "方法" in moji print(a) # True a = "とは、、" in moji print(a) # False # replaceで置換する moji2 = moji.replace("文字列", "モジレツ") print(moji2) # 正規表現とは、モジレツの集合を一つのモジレツで表現する方法の一つである。 |
正規表現で行う
正規表現を使う場合、reモジュールをインポートします。
以下、よく使いそうなものだけ抜粋します。
詳しくはhttps://docs.python.jp/3/library/re.html参照。
search
マッチオブジェクト = re.search(パターン, 探す文字列[, flags=0])
文字列を走査し、パターンがマッチする最初の場所を探して、対応するマッチオブジェクトを返します。
文字列内にパターンにマッチする場所が無い場合は None を返します。
flagsはオプションですが、後述しますが、複数行でマッチさせる場合等の指定を書き加えられます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import re moji = "正規表現とは、文字列の集合を一つの文字列で表現する方法の一つである。" # 「文字列」を探す # マッチした時はaにマッチオブジェクトが入る a = re.search("文字列", moji) if a: print("あります") else: print("ありません") # 「モジレツ」を探す a = re.search("モジレツ", moji) # ない時はaにはNoneが入る if a: print("あります") else: print("ありません") |
結果
あります
ありません
この場合、パターンに通常の文字をそのまま入れて探していますので、正規表現を使う意味はあまりありません(文字列メソッドやin演算子でできます)。
1 2 3 4 5 6 7 8 9 10 11 |
import re moji = "正規表現とは、文字列の集合を一つの文字列で表現する方法の一つである。" # 「文..」を探す # ある時はaにはマッチオブジェクトが入る a = re.search(r"文..", moji) if a: print("あります") else: print("ありません") |
結果
あります
変数mojiの中に「文..」という文字はないのにもかかわらず、「文..」が「あります」になりました。
まず、r”文..”のrですが、これは以前書きましたraw文字列で、正規表現のパターンを記述する時は書いた方が便利です。
そして「文..」のドット「.」ですが、これは任意の1文字という意味です。この「.」のような特殊文字が色々ありまして、通常の文字と特殊文字を組み合わせてパターンを書きます。
1 2 3 4 5 6 7 8 9 10 |
import re moji = "正規表現とは、文字列の集合を一つの文字列で表現する方法の一つである。" a = re.search(r"表現..、", moji) if a: print(a.group()) else: print("ありません") |
結果
表現とは、
表現という文字は2箇所出てきますが「表現..、」にマッチするのは「表現とは、」だけであり、マッチオブジェクトのgroupメソッドでマッチした文字列の取得をしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import re moji = "正規表現とは、文字列の集合を一つの文字列で表現する方法の一つである。" # 1 a = re.search(r"(文.+)の", moji) if a: print(a.group(1)) else: print("ありません") # 2 a = re.search(r"(文.+?)の", moji) if a: print(a.group(1)) else: print("ありません") |
結果
文字列の集合を一つの文字列で表現する方法
文字列
#1で「(文.+)の」のパターンで、丸括弧は一つのグループで、group(1)で最初の丸括弧のグループを取得します。
「+」は前の文字の一回以上の繰り返しで、「文」で始まり、任意の文字の一回以上の繰り返しで、「の」までにマッチするものを探します。
#1と#2のパターンはほぼ同じですが、「(文.+?)の」の?だけが違います。この場合の?は最短マッチを意味します。
結果を見て、最短マッチされていることを確認して下さい。
findall
リスト = re.findall(パターン, 探す文字列[, flags=0])
searchは複数マッチしても最初の部分しか返しません。複数のマッチを取得するにはfindallを使います。
以下、Yahoo!ニュースのrssから記事のタイトルとリンクをfindallで抜き出してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
import re rss = """<?xml version='1.0' encoding='UTF-8'?> <rss xmlns:blogChannel="http://backend.userland.com/blogChannelModule" version="2.0"> <channel> <title>Yahoo!ニュース・トピックス - 科学</title> <link>https://news.yahoo.co.jp/hl?c=c_sci</link> <description>Yahoo! JAPANのニュース・トピックスで取り上げている最新の見出しを提供しています。</description> <language>ja</language> <pubDate>Tue, 24 Oct 2017 09:06:21 +0900</pubDate> <item> <title>酒で外国語うまく 実験で確認</title> <link>https://news.yahoo.co.jp/pickup/6258538</link> <pubDate>Mon, 23 Oct 2017 19:59:06 +0900</pubDate> <enclosure length="133" url="https://s.yimg.jp/images/icon/photo.gif" type="image/gif"> </enclosure> <guid isPermaLink="false">yahoo/news/topics/6258538</guid> </item> <item> <title>「若いダイヤ」発見 業界騒然</title> <link>https://news.yahoo.co.jp/pickup/6258531</link> <pubDate>Mon, 23 Oct 2017 18:34:48 +0900</pubDate> <enclosure length="133" url="https://s.yimg.jp/images/icon/photo.gif" type="image/gif"> </enclosure> <guid isPermaLink="false">yahoo/news/topics/6258531</guid> </item> <item> <title>炭水化物抜き 潜む落とし穴</title> <link>https://news.yahoo.co.jp/pickup/6258484</link> <pubDate>Mon, 23 Oct 2017 12:34:15 +0900</pubDate> <enclosure length="133" url="https://s.yimg.jp/images/icon/photo.gif" type="image/gif"> </enclosure> <guid isPermaLink="false">yahoo/news/topics/6258484</guid> </item> <item> <title>腸で口内細菌増えると潰瘍に?</title> <link>https://news.yahoo.co.jp/pickup/6258314</link> <pubDate>Sun, 22 Oct 2017 15:37:33 +0900</pubDate> <guid isPermaLink="false">yahoo/news/topics/6258314</guid> </item> <item> <title>10月なのに超大型台風 なぜ</title> <link>https://news.yahoo.co.jp/pickup/6258248</link> <pubDate>Sat, 21 Oct 2017 22:45:21 +0900</pubDate> <enclosure length="133" url="https://s.yimg.jp/images/icon/photo.gif" type="image/gif"> </enclosure> <guid isPermaLink="false">yahoo/news/topics/6258248</guid> </item> <item> <title>台風観測「目」に計器を投下</title> <link>https://news.yahoo.co.jp/pickup/6258186</link> <pubDate>Sat, 21 Oct 2017 12:24:18 +0900</pubDate> <enclosure length="133" url="https://s.yimg.jp/images/icon/photo.gif" type="image/gif"> </enclosure> <guid isPermaLink="false">yahoo/news/topics/6258186</guid> </item> <item> <title>白亜紀のカエル 恐竜を捕食か</title> <link>https://news.yahoo.co.jp/pickup/6258276</link> <pubDate>Sun, 22 Oct 2017 09:20:48 +0900</pubDate> <enclosure length="133" url="https://s.yimg.jp/images/icon/photo.gif" type="image/gif"> </enclosure> <guid isPermaLink="false">yahoo/news/topics/6258276</guid> </item> <item> <title>若者にもスマホ老眼 防ぐには</title> <link>https://news.yahoo.co.jp/pickup/6258196</link> <pubDate>Sat, 21 Oct 2017 15:15:56 +0900</pubDate> <enclosure length="133" url="https://s.yimg.jp/images/icon/photo.gif" type="image/gif"> </enclosure> <guid isPermaLink="false">yahoo/news/topics/6258196</guid> </item> </channel> </rss> """ pattern = r"<item>.*?<title>(.*?)</title>.*?<link>(.*?)</link>.*?</item>" a = re.findall(pattern, rss, flags=(re.MULTILINE | re.DOTALL)) if a: for v in a: title, link = v print(title) print(link) else: print("ありません") |
結果
酒で外国語うまく 実験で確認
https://news.yahoo.co.jp/pickup/6258538
「若いダイヤ」発見 業界騒然
https://news.yahoo.co.jp/pickup/6258531
炭水化物抜き 潜む落とし穴
https://news.yahoo.co.jp/pickup/6258484
腸で口内細菌増えると潰瘍に?
https://news.yahoo.co.jp/pickup/6258314
10月なのに超大型台風 なぜ
https://news.yahoo.co.jp/pickup/6258248
台風観測「目」に計器を投下
https://news.yahoo.co.jp/pickup/6258186
白亜紀のカエル 恐竜を捕食か
https://news.yahoo.co.jp/pickup/6258276
若者にもスマホ老眼 防ぐには
https://news.yahoo.co.jp/pickup/6258196
パターンが長くなってきましたので、pattern変数に入れています。
itemタグ内でtitleとlinkを抜き出します。「(.*?)」*は0回以上の繰り返しです。
findallの戻り値はリスト型になりますが、丸括弧グループが2つ以上ある場合は、リストの要素はタプルになります。
このように返ってきます。
[
(‘酒で外国語うまく 実験で確認’, ‘https://news.yahoo.co.jp/pickup/6258538’),
(‘「若いダイヤ」発見 業界騒然’, ‘https://news.yahoo.co.jp/pickup/6258531’),
・
・
・
]
flags=(re.MULTILINE | re.DOTALL)は、MULTILINEは複数行対応、DOTALLは特殊文字「.」を、改行を含むどんな文字にもマッチさせるということです。
sub
正規表現で文字列を置換する場合はsubを使います。
置換後の文字列 = re.sub(パターン, 置換する文字列, 置換される文字列[, count=0][, flags=0])
count は、置換される回数、もし省略されるかゼロであればすべて置換。
flagsは前と同じ。
パターンに該当しない場合は、元の文字列がそのまま返ります。
1 2 3 4 5 6 7 |
import re moji = "正規表現とは、文字列の集合を一つの文字列で表現する方法の一つである。" a = re.sub(r"文..", "モジレツ", moji) print(a) |
結果
正規表現とは、モジレツの集合を一つのモジレツで表現する方法の一つである。
パターンでグループ化した正規表現が、第二引数の内部で\1や\2などのような特殊文字で「後方参照」することが出来ます。
1 2 3 4 5 6 7 |
import re moji = "正規表現とは、文字列の集合を一つの文字列で表現する方法の一つである。" a = re.sub(r"(正...).*?(文..).*?(.{6})$", r"\1は\2の\3", moji) print(a) |
結果
正規表現は文字列の一つである。
第一引数で丸括弧を3つグループ化しています。それぞれを第二引数内の\1、\2、\3で参照しています。
「(.{6})$」これは、任意の文字6文字が末尾にマッチ($)するものということです。
正規表現のパターンの書き方は一朝一夕には覚えられません。
しかし、正規表現のパターンの書き方はPythonだけではなくて、色々なプログラミング言語でほぼ共通なので、覚えておくと便利です。