こんにちは、くまです。
今回はpythonの「正規表現」を解説していきます。
正規表現はpython初心者の方にとって難しいですよね。
私も最初、正規表現でかなり苦戦しました。
使う表現のみ限定して、分かりやすい説明が欲しかったり
例文多めに書いてもらえればもっと分かりやすいのに…と思っていました。
その頃を思い出して、正規表現について解説しました。
初心者の方が正規表現は
・どんなものか
・どう使えばいいか
をすぐ理解できるように解説しました。
正規表現とは?
正規表現は一般的に複数の文字列をより簡単に一つの形式で表現するための表現方法のことです。
と言われても分かりにくいです。
ちょっとした具体例を交えて説明します。
例えば、文字列から「abx」、「aby」、「abz」の三つの単語を取り出したいとします。
冗長ですが、分かりやすい書き方は以下の形です。
x = 'abxabyabz'
print(re.findall('abx|aby|abz',x))
>>['abx', 'aby', 'abz']
それぞれ三つ「abx」「aby」「abz」と書いて取り出すよりも、正規表現で「ab[x-z]」と書いた方が明らかに短くすっきりします。
x = 'abxabyabz'
print(re.findall('ab[x-z]+',x))
>>['abx', 'aby', 'abz']
このように複数の文字列(「abx」「aby」「abz」)を短くすっきりさせるために、一つの形式(「ab[x-y]」)で表現する方法が正規表現です。
次は数字を例として、より分かりやすく正規表現を説明します。
正規表現の具体例
例えば以下のような文字列があったとします。
s = 'prince:1000yen'
この価格となる数字「1000」のみを抽出したい場合、一番長ったらしいですが、分かりやすい書き方は
print(re.search('[0123456789]+',s))
>> <re.Match object; span=(6, 10), match='1000'>
となります。
match=‘1000’で欲しい数字が出てきますが、これでは長い。
そのため、[0123456789]を短くした[0-9]という書き方があります。
print(re.search(‘[0-9]+’,s))
>> <re.Match object; span=(6, 10), match='1000'>
もっと短くすると[\d]という書き方になります。これは[0-9]と同じ意味です。
print(re.search('[\d]+',s))
>> →<re.Match object; span=(6, 10), match='1000'>
この[\d]は全角数字にも使えます。
S = 'price:1000yen'
print(re.search('[\d]+',S))
>> <re.Match object; span=(6, 10), match='1000'>
[\d]だけで「01234567890123456789」をカバーできます。
これも複数の文字列を一つの形式で表す正規表現です。
では、ここから[\d]のような正規表現の特殊シーケンスの一覧を紹介します。
正規表現の特殊シーケンス
表現 | 同じ意味 | 意味 |
\d | [0-9] | 任意の数字(全半角) |
\D | [^0-9] | 任意の数字以外 |
\w | [a-zA-Z0-9_] | 任意の英文字(全半角)と数字(全半角)とアンダースコア |
\W | [^a-zA-Z0-9_] | 任意の英文字と数字とアンダースコア以外 |
以上が一般的な特殊シーケンスの一覧です。
ここから一つずつ例文を紹介します。
一度に多くに該当が取れるようにfindall関数を使用した例文になります。
- \d と \D (数字と数字以外)
s = 'tel:012-3456-7890'
print(re.findall('[\d]+',s))
>> ['012', '3456', '7890']
print(re.findall('[\D]+',s))
>> ['tel:', '-', '-']
- \w と \W (英文字、数字、アンダースコアと英文字、数字、アンダースコア以外)
s = 'Name:YAMADA_TARO Mail:test@mail.com Tel:012-3456-7890'
print(re.findall('[\w]+',s))
>> ['Name', 'YAMADA_TARO', 'Mail', 'test', 'mail', 'com', 'Tel', '012', '3456', '7890']
print(re.findall('[\W]+',s))
>> [':', ' ', ':', '@', '.', ' ', ':', '-', '-']
正規表現の特殊シーケンスの例外(英字のみと日本語対応)
ここから少し例外を紹介します。
上記の特殊シーケンスでは取りにくい英文字のみや日本語対応を下記にまとめてみました。
表現 | 意味 |
[a-z] | 任意の半角英小文字 |
[A-Z] | 任意の半角英大文字 |
[ぁ-ゟ] | 任意のひらがな |
[ァ-ヿ] | 任意のカタカナ |
[一-龥] | 任意の漢字 |
s = 'Python, ひらカナ, 正規表現'
# 英小文字
print(re.findall('[a-z]+',s))
>> ['ython']
# 英大文字
print(re.findall('[A-Z]+',s))
>> ['P']
# ひらがな
print(re.findall('[ぁ-ゟ]+',s))
>> ['ひら']
# カタカナ
print(re.findall('[ァ-ヿ]+',s))
>> ['カナ']
# 漢字
print(re.findall('[一-龥]+',s))
>> ['正規表現']
エスケープシーケンス
エスケープシーケンスとは、バックスラッシュ「 \ 」で始まる通常の文字列で表せない特殊な文字や機能を特定の形式で表したものです。
つまりは、空白や改行コードなどです。
このエスケープシーケンスも正規表現の一部になります。また、次のraw文字列でもエスケープシーケンスは出てきます。
そのエスケープシーケンスの一部を一覧で並べます。
表現 | 意味 |
\s | 空白文字(半角スペースや\n\t\r\f) |
\S | 空白文字以外 |
\t | タブ文字 |
\n | 改行文字 |
\r | リターン |
\f | 改ページ |
\\ | バックスラッシュ |
正規表現のメタ文字
今までは数字、英文字など特定の種類の正規表現を説明していきました。
次は、より便利な使い方ができる「メタ文字」です。
メタ文字とは、正規表現では本来の文字の意味とは異なる意味を持つ文字です。
そのメタ文字の一覧を並べます。
表現 | 意味 | 使用例 | 該当例 |
. | 任意の文字 (英数字ひらカナ漢字) | a.c | aac, abc, a1c aあc など |
* | 前の文字を0回以上繰り返す | a.*c | ac, abc, abccc, a123c, aあ〜いc など 「.*」で任意の文字を0回以上繰り返し |
+ | 前の文字を1回以上繰り返す | a(\d)+c | a1c, a123cはOK acやabcはNG 「\d」は0-9で1回以上繰り返し |
? | 前の文字を0回か1回表示 | ac? | a, acのみOK 「https?」はhttp, httpsとなる |
{ m } | 前の文字をm回繰り返す | a{2}c | aacのみOK ac, aaac, accはNG |
{ m,n } | 前の文字をm~n回繰り返す | ab{2,4}c | abbc, abbbc, abbbbcのみ |
[ ] | [ ]内の任意の1文字を示す | [0-9] | 1, 3, 8, 9などはOK 12や100など複数はNG |
( ) | ( )内をまとまりとする | (123)* | 123, 123123など |
^ | 文字列の先頭を示す | ^a(\w)* | a, abc, a12_などはOK b12はNG |
$ | 文字列の末尾を示す | (\d)*x$ | 123x, 0xなど |
正規表現とraw文字列
特定文字を抽出したい文字列を囲むシングルクオテーションかダブルクオテーションの前に、「r」か「R」をつけるとエスケープシーケンスが無効化されます。
無効化というと分かりにくいですが、簡単に言えば、バックスラッシュもそのまま表示されます。
raw文字列はこんな感じの文字列です。
s = r'I\'m a man.'
エスケープシーケンスは上記の
\t (タブ文字)、\n (改行文字)、\r (リターン)、\f (改ページ)
だけではなく、
\’ (シングルクオテーション(‘))、\” (ダブルクオテーション(”))
なども含まれます。
つまり、文字列の中に「’(シングルクオテーション)」を入れたい場合は
print('I\'m a man')
>> I’m a man
となります。これがエスケープシーケンスの一例です。
しかし、Raw文字列でrをつけるとエスケープシーケンスが無効化されるため
print(r'I\'m a man')
>> I\'m a man
となります。え、文がおかしくなってダメじゃん。と思いますが、本来はこういう時に使います。
print(r’C:\\User\Python\test’)
>> C:\\User\Python\test
これなら問題ないです。
では、「\d」や「\w」など特殊シーケンスではどうなるか?
これは簡単で、エスケープシーケンスではないため、Raw文字列をやっても効果がないです。
pythonのreモジュール
正規表現をよく使用する「re」モジュールの一部を紹介していきます。
分かりやすいように機能別です。「置換」「抽出」「分割」の3つです。
置換
reモジュールの中で置換する関数は「sub」です。
どのように使うかというと
import re
re.sub('置換したい部分の正規表現', '置換後の文字', '文字列')
s = '0123-456-789'
print(re.sub('(\d)', 'x', s))
>> xxxx-xxx-xxx
抽出
文字列の先頭パターンが一致した場合に抽出する「match」
import re
re.match('正規表現', '文字列')
s = '100-1010'
print(re.match('(\d){3}', s))
>> <re.Match object; span=(0, 3), match='100'>
t = 'postcode100-1010'
print(re.match('(\d){3}', t))
>> None
文字列の先頭パターンに限らず一致した最初の部分を抽出する「search」
import re
re.search('正規表現', '文字列')
s = '100-1010'
print(re.search('(\d){3}', s))
>> <re.Match object; span=(0, 3), match='100'>
t = 'postcode100-1010'
print(re.search('(\d){3}', t))
>> <re.Match object; span=(8, 11), match='100'>
文字列の一致した部分を全てリスト化して抽出する「findall」
import re
re.findall('正規表現', '文字列')
s = '100-1010'
print(re.findall('\d*', s))
['100', '1010']
分割
正規表現で一致した部分で分割を行う「split」
import re
re.split('正規表現','文字列')
s = '0123-456-789'
print(re.split('\W',s))
>> ['0123', '456', '789']
最後に
今までpythonの正規表現とreモジュールを紹介していきました。
「習うより慣れろ」という言葉がある通り、実際に使って覚えて自分のものにしていってください。
また、よりpythonを学ぶたい人に向けて、全般的に広く学べる「PyQ」を紹介します。
この「PyQ」は初心者向けの「ifの書き方」から「各種のモジュールの使い方」まで幅広くカバーしています。もちろん、今回の記事の「reモジュール」も「正規表現」も載っています。
それと、pythonのWebアプリケーションフレームワーク「Django」の使い方や「Webスクレイピング」、「機械学習」のやり方も網羅されています。
もし興味のある方は以下のリンクから「PyQ」を見てみてください。
オンラインPython学習サービス「PyQ™(パイキュー)」また、このブログでは「Django」に関して多く記事を書いています。
これからDjangoを学びたい方がいらしたら、下記の初心者向けの記事から学んでみてください。