Pythonは、さまざまな活用ができるため「データ整理などで活用したい」と考える方も多いのではないでしょうか。しかし、そんな方の中には
・ Pythonでのデータ整理の方法がよくわからない
・ 「正規表現」というものがどのようなものかわからない
と悩まれている方も多いでしょう。そのため、この記事では
・ Pythonにおける正規表現の使い方
・ 正規表現の表現方法の基礎
などについて分かりやすく解説します。
この記事を見れば、Pythonの初心者の方や正規表現がわからない方も、基本的な知識を知ることができます。さらに、Udemyの講座を併用すれば、より深い知識と技術を身につけることも可能です。
公開日:2024年2月21日
Pythonの正規表現とは:文字列の抽出や置換を行う手法
「正規表現」とは、文字列の抽出や操作をするための表現・記述方法です。
抽出や置換などを行うための検索のパターンを予め指定し、パターンに一致する文字を操作することができます。正規表現は、Python独自の機能ではなく、さまざまなプログラミング言語などで利用可能です。Pythonでは正規表現のためのモジュールが用意されているため、簡単に正規表現を用いてデータの整理などが行なえます。
Pythonの正規表現が使用される場面
Pythonで正規表現が活用できるようになれば、次のような場面で役立てることが可能です。
- 文章の中から電話番号やE-mailアドレスを抽出する
- テキストデータの中から特定の文字列のみを置換する
- 大量のデータから妥当性を検証する(バリデーション)
Pythonの正規表現を活用することで、大量データの整理や管理、修正も短時間で行なえます。例えば、大量のHTMLタグが記述されたテキストデータで、特定のHTMLタグのみを修正する、ということも短時間で簡単に実行することが可能です。
\文字より動画で学びたいあなたへ/
Udemyで講座を探す >Pythonの正規表現の使い方
ここからは、Pythonの正規表現の使い方を具体的に解説します。まずは、基本的な流れから分かりやすく解説するため、一つずつ見ていきましょう。
reモジュールをインポート
Pythonで正規表現(regex)を使うためには「reモジュール」をインポートする必要があります。reモジュールは標準ライブラリであるため、別途追加で用意する必要はありません。Pythonで正規表現を利用する際には、次のようにはじめにインポートしておきましょう。
1 |
import re |
対象となる文字列の導入
次に、検索・抽出・置換を行う文字列を用意します。ここでは、例として次の文字列を用意しました。
1 |
text_data = "This is an example with phone numbers: 123-456-7890 and 987-654-3210." |
正規表現の関数を用いて操作
reモジュールの関数を用いて、先程の文字列を操作します。ここでは「findall関数」を例として利用します。次のコードで示される関数やメタ文字などについては、後ほど詳しく解説します。
1 |
phone_number = re.findall(r'\d{3}-\d{3}-\d{4}', text_data) |
これで、変数「phone_number」に検索結果の文字列が格納されるため、print文を使って出力します。ここまでの内容をすべて含んだコードは次のとおりです。
1 2 3 4 5 6 7 8 9 |
import re text_data = "This is an example with phone numbers: 123-456-7890 and 987-654-3210." phone_number = re.findall(r'\d{3}-\d{3}-\d{4}', text_data) for pnum in phone_number: print("Phone numbers found:", pnum) |
出力結果:
1 2 |
Phone numbers found: 123-456-7890 Phone numbers found: 987-654-3210 |
このように、正規表現を用いることでテキストデータの中から簡単に求めるデータを抽出したりすることが可能です。
正規表現の関数の種類
reモジュールをインポートすることで、次に紹介する関数が使えるようになります。正規表現のパターンにマッチした文字列をどのように処理するかによって、適切な関数を選択するとよいでしょう。
ここでは、利用頻度が高い以下の関数について、それぞれ最も基礎的な使い方を簡単に解説します。
match関数 | 先頭で一致するものを検索 |
search関数 | 全体の中から一致する最初のものを抽出 |
findall関数 | 全体から一致するものをすべて抽出 |
finditer関数 | 全体の中から一致するものをすべて抽出 |
fullmatch関数 | 完全に一致するものを抽出 |
sub関数 | 一致した文字列を置換 |
より詳しく利用方法について知りたい方は、公式ドキュメントをご確認ください。
match関数:先頭で一致するものを検索
match関数は、文字列の先頭が正規表現のパターンに一致するかどうかを調べる関数です。パターンに一致しない場合には「None」が返されます。
構文 | 戻り値 (マッチする場合) |
戻り値 (マッチしない場合) |
re.match(pattern, string) | オブジェクト | None |
一致するものが存在する場合
phone_numberには、電話番号が格納されています。正規表現では080または090からはじまり、-(ハイフン)を挟んで数字が4桁+4桁のものがマッチするかどうかを調べます。
1 2 3 4 5 6 7 8 9 10 11 |
import re phone_number = "080-1112-2223, 090-4445-6667, 0120-111-111" result = re.match(r'0[8|9]0-[0-9]{4}-[0-9]{4}', phone_number) if result : print(result.group()) else: print(result) |
出力結果:
1 |
080-1112-2223 |
今回はphone_numberの先頭にマッチする文字列が存在しているため、マッチした文字列を出力できました。
一致するものが存在しない場合
phone_number2は、文字列の先頭が080や090ではありません。match関数では先頭が一致するかどうかを判別するため、一致するものが存在しないとして出力されます。
1 2 3 4 5 6 7 8 9 10 11 |
import re phone_number2 = "0120-111-111, 080-1112-2223, 090-4445-6667" result = re.match(r'0[8|9]0-[0-9]{4}-[0-9]{4}', phone_number2) if result : print(result.group()) else: print(result) |
出力結果:
1 |
None |
search関数:全体の中から一致する最初のものを抽出
search関数は、全体の中から一致する最初のものを抽出します。match関数とは異なり、文字列の先頭が一致していなくとも抽出することが可能です。一致する文字列が存在しない場合は、match関数と同様に「None」が返されます。
構文 | 戻り値 (マッチする場合) |
戻り値 (マッチしない場合) |
re.search(pattern, string) | オブジェクト | None |
一致するものが存在する場合
先程のコードと似ていますが、今回は0120から始まる文字列をパターンとしています。phone_numberには0120から始まる文字列が2つ含まれていますが、最初にマッチした「0120-111-111」のみが抽出されます。このとき、match関数とは異なり、文字列の先頭がマッチしていなくとも出力できていることに注目です。
1 2 3 4 5 6 7 8 9 10 11 |
import re phone_number = "080-1112-2223, 0120-111-111, 090-4445-6667, 0120-222-222" result = re.search(r'0120-[0-9]{3}-[0-9]{3}', phone_number) if result : print(result.group()) else: print(result) |
出力結果:
1 |
0120-111-111 |
一致するものが存在しない場合
search関数の場合は、単純に文字列の中にパターンと一致するものが存在しなければNoneが返されます。
1 2 3 4 5 6 7 8 9 10 11 |
import re phone_number2 = "080-1112-2223, 090-4445-6667" result = re.search(r'0120-[0-9]{3}-[0-9]{3}', phone_number2) if result : print(result.group()) else: print(result) |
出力結果:
1 |
None |
findall関数:全体から一致するものをすべて抽出
findall関数は、指定した正規表現のパターンにマッチする文字列をすべて抽出します。他の関数と違い、リストに格納する点に注意してください。単純にマッチする文字列をすべて抽出したい場合には、とても使い勝手の良い関数です。パターンにマッチする文字列が存在しない場合は空のリストが返されます。
構文 | 戻り値 (マッチする場合) |
戻り値 (マッチしない場合) |
re.findall(pattern, string) | リスト | 空のリスト |
一致するものが存在する場合
以下のサンプルコードは前述のコードと同じです。英数字が混ざったtext_dataから「3桁-3桁-4桁」の数字をパターンとして抽出しています。このとき、phone_numberはリストとなり、for文を使ってリストの中身をすべて出力しています。
1 2 3 4 5 6 7 8 9 |
import re text_data = "This is an example with phone numbers: 123-456-7890 and 987-654-3210." phone_number = re.findall(r'\d{3}-\d{3}-\d{4}', text_data) for pnum in phone_number: print("Phone numbers found:", pnum) |
出力結果:
1 2 |
Phone numbers found: 123-456-7890 Phone numbers found: 987-654-3210 |
今回はphone_numberの先頭にマッチする文字列が存在しているため、マッチした文字列を出力できました。
一致するものが存在しない場合
先程のtext_dataの中身を少し変更し、「4桁-3桁-3桁」の数字にするとマッチしなくなります。if文を使ってphone_numberが空かどうかをチェックし、空の場合はnot foundと出力するようにしました。その結果、想定通りnot foundが出力されています。
1 2 3 4 5 6 7 8 9 |
import re text_data2 = "This is an example with phone numbers: 0120-999-999 and 0120-888-888." phone_number = re.findall(r'\d{3}-\d{3}-\d{4}', text_data2) if phone_number == [] : print("not found") |
出力結果:
1 |
not found |
finditer関数:全体の中から一致するものをすべて抽出
finditer関数も、findall関数と同様にパターンに一致するものをすべて抽出できます。ただし、findall関数とは異なり、戻り値はリストではなくオブジェクト(イテレータ)です。finditer関数では、各一致に対してmatchオブジェクトが提供され、一致したテキストの位置情報なども含まれます。そのため、findall関数と異なり、パターンに一致した文字列の開始位置や終了位置も取得することが可能です。
構文 | 戻り値 (マッチする場合) |
戻り値 (マッチしない場合) |
re.finditer(pattern, string) | オブジェクト |
一致するものが存在する場合
比較のためにfindall関数と同じようなサンプルコードで確認してみましょう。基本的には同じようなコードとなっていますが、取り出し方が異なります。加えて、パターンの一致した文字列の開始位置と終了位置も併せて出力するようにしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import re text_data = "This is an example with phone numbers: 123-456-7890 and 987-654-3210." m = None phone_number = re.finditer(r'\d{3}-\d{3}-\d{4}', text_data) for m in phone_number: print("Phone numbers found:", m.group(), ", found_position:", m.span()) if m is None: print("not found") |
出力結果:
1 2 |
Phone numbers found: 123-456-7890 , found_position: (39, 51) Phone numbers found: 987-654-3210 , found_position: (56, 68) |
一致するものが存在しない場合
パターンに一致するものが存在しない場合の判定をしたい場合は、イテレータを使って取り出したマッチオブジェクトを格納する変数(この例ではm)にNoneを格納しておきます。for文を使ってマッチオブジェクトを取り出せなかった場合はNoneのままになるため、以下のサンプルコードでは「not found」が出力されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import re text_data2 = "This is an example with phone numbers: 0120-999-999 and 0120-888-888." m = None phone_number = re.finditer(r'\d{3}-\d{3}-\d{4}', text_data2) for m in phone_number: print("Phone numbers found:", m.group(), ", found_position:", m.span()) if m is None: print("not found") |
出力結果:
1 |
not found |
fullmatch関数:完全に一致するものを抽出
fullmatch関数は、正規表現のパターンが文字列全体に完全に一致するか検証します。完全一致の場合のみオブジェクトを返し、一致しない場合はNoneが返されます。
構文 | 戻り値 (マッチする場合) |
戻り値 (マッチしない場合) |
re.fullmatch(pattern, string) | オブジェクト | None |
以下のサンプルコードでは、「Tで始まりoで終わる文字列」を検証しています。「Tokyo」と「Tokyo, Kyoto」は文字列全体が完全一致するため、resultにオブジェクトが格納されて中身を取り出して出力することが可能です。一方「Tokyo, Oosaka」は完全一致とはならないためNoneが返されます。
1 2 3 4 5 6 7 8 9 10 11 12 |
import re text_list = ["Tokyo", "Tokyo, Kyoto", "Tokyo, Oosaka"] for l in text_list: result = re.fullmatch(r'^T.*o$', l) if result is not None: print("[", result.group(), "] is match") else: print("[", l, "] is not match") |
出力結果:
1 2 3 |
[ Tokyo ] is match [ Tokyo, Kyoto ] is match [ Tokyo, Oosaka ] is not match |
sub関数:一致した文字列を置換
sub関数は、正規表現のパターンに一致した文字列を置換して返します。
構文 | 戻り値 (マッチする場合) |
戻り値 (マッチしない場合) |
re.sub(pattern, repl, string, count) | 変換後の文字列 | そのままの文字列 |
以下のサンプルコードでは「dog」を「cat」に置換しています。もちろんこれらは正規表現で指定することも可能です。1つ目は単純に置換しようとしていますが、デフォルトでは大文字と小文字は区別されるため「Dogs」が変換できていません。
そこで、2つ目では「flags=re.IGNORECASE」を記述し、大文字と小文字を区別しないように指定することでDogsも置換することができています。また、3つ目では置換する回数を「1」に指定したため、出力結果は1つ目と同じになりました。
最後の4つ目は「rabbit」という文字列を検索して置換しようとしていますが、元の文字列には存在しないパターンです。この場合は、元の文字列がそのまま出力されていることが分かります。
1 2 3 4 5 6 7 8 9 10 11 12 |
import re text_data = "I like dogs. Dogs are so cute." print(re.sub('dog', 'cat', text_data)) #1 print(re.sub('dog', 'cat', text_data, flags=re.IGNORECASE)) #2 print(re.sub('dog', 'cat', text_data, 1, flags=re.IGNORECASE)) #3 print(re.sub('rabbit', 'cat', text_data)) #4 |
出力結果:
1 2 3 4 |
I like cats. Dogs are so cute. I like cats. cats are so cute. I like cats. Dogs are so cute. I like dogs. Dogs are so cute. |
正規表現の表現方法
正規表現では「数字」や「英数字」、「任意の文字」、「文字列の先頭」などを表す表現方法があります。正規表現を使いこなす上で欠かせないものであり、これらは「メタ文字」や「特殊シーケンス」と呼ばれるものです。よく利用するものをそれぞれ表でまとめたため、一つずつ見ていきましょう。
メタ文字
メタ文字とは、特定の意味を持つ特殊記号です。前述のサンプルコードの中にもいくつか登場しています。メタ文字を用いることで、複雑なパターンを表現することができます。
メタ文字 | 意味 | 記述例 | マッチする文字列 |
. | 改行以外の任意の文字 | 1.3 | 123,153,1a3… |
^ | 文字列の先頭 | ^12 | 123,125,12b… |
$ | 文字列の末尾 | 12$ | 12,912,c12… |
* | 直前のパターンを0回以上繰り返したもの | 12* | 1,12,123,1234… |
+ | 直前のパターンを1回以上繰り返したもの | 12+ | 12,123,1234… |
? | 直前のパターンを0または1回繰り返したもの | 12? | 1,12 |
{m} | 直前のパターンをm回繰り返したもの | x{3} | xxx |
{m,n} | 直前のパターンをm回からn回繰り返したもの | x{1,4} | x,xx,xxx,xxxx |
[] | []内のいずれか1文字 | [abc] [3-6] | a,b,c 3,4,5,6 |
A|B | AとBのいずれか | x|y | x,y |
() | グループ化 ※他のメタ文字と組み合わせる |
(abc). (abc)$ | abcd,abc3… xyzabc,123abc… |
メタ文字のなかでも「^」と「[]」の組み合わせには注意が必要です。[]は文字の集合を意味するメタ文字ですが、この中で^を使うと「否定」の意味になります。[]の内外に^がある場合では次のように意味が異なります。
意味 | 記述例 | マッチする文字列 |
0-9以外(数字以外) | [^0-9] | a,b,c… |
0-9ではじまる | ^[0-9] | 0,1,2,3… |
特殊シーケンス
メタ文字だけであらゆるパターンを表現できますが、特殊シーケンスを用いればより簡単に表現できる場合があります。特殊シーケンスは特に利用頻度の高いパターンを簡潔に呼び出せるようにしたものであり、「\(バックスラッシュ)」とアルファベット一文字を組み合わせて表現します。
特殊シーケンス | 意味 |
\d | 任意の数字 |
\D | 任意の数字以外 |
\s | 任意の空白文字 |
\S | 任意の空白文字以外 |
\w | 任意の英数字 |
\W | 任意の英数字以外 |
メタ文字だけでなく特殊シーケンスも覚えておけば、より簡潔に正規表現を使うことが可能です。また、「\(バックスラッシュ)」は、メタ文字と組み合わせるとメタ文字そのものを表せます。例えば、パターンとして「*」をマッチさせたい場合には「\*」とすることで「*」がメタ文字ではなく単なる文字として処理されます。これは「エスケープ処理」と呼ばれるものです。
Pythonの正規表現を使いこなそう!
Pythonでデータ整理やバリデーションを実施する際には、正規表現は欠かせません。この記事では、Pythonで正規表現を利用するための基礎を紹介したため、この記事を参考に正規表現について理解を深めましょう。
「もっとPythonの基礎から学びたい」という方には、以下の講座がおすすめです。Pythonの基本操作から動画を使って解説しているため、体系的に基礎知識を身につけることができます。
現役シリコンバレーエンジニアが教えるPython 3 入門 + 応用 +アメリカのシリコンバレー流コードスタイル
現役シリコンバレーエンジニアが教えるPython入門!応用では、データ解析、データーベース、ネットワーク、暗号化、並列化、テスト、インフラ自動化、キューイングシステム、非同期処理など盛り沢山の内容です!
\無料でプレビューをチェック!/
講座を見てみる評価:★★★★★
pythonの王道のコードを学べただけでなく、さまざまなライブラリの使い方を知ることができました。
評価:★★★★★
一つ一つの項目が必要十分な単位でまとめられているため、後ほど再度学習したい場合に便利です。
正規表現と併せて、Pythonを使いこなすために基礎から学んでみてはいかがでしょうか。
最新情報・キャンペーン情報発信中