正規表現とは
プログラムを書いていると、
「この文字列の中からメールアドレスだけ取り出したい」
「入力された値が正しい形式かどうか確認したい」
という場面はよくあります。
そのたびに、if文やforループで地道に処理を書くのは、なかなか骨が折れます。そこで役立つのが正規表現です。
正規表現とは、「こういう形の文字列を探してほしい」というパターンを、短い記号の組み合わせで表現する仕組みのことです。
まずは正規表現がどんな考え方に基づいているのか、どんなケースで使われるのか見ていきましょう。
【関連】Pythonのstr型を基礎から学ぶ!初心者向け完全ガイド
【関連】Pythonの空白処理をマスターしよう!strip・split・空白判定の使い方を徹底解説
【関連】Pythonのstrip関数とは?初心者でもすぐに理解できる文字列整形
正規表現の基本的な考え方
正規表現を使うと、文字列を「丸ごと指定して探す」のではなく、「こういう形の文字列」というパターンで探せるようになります。
例えば「数字が3桁続く」という条件は、\d{3}という短い記法で表せます。データの形式が多少違っていても柔軟に対応できるのが、正規表現の大きな魅力です。
出力結果
['100']正規表現を使う場面
正規表現が役立つのは、「特定の形式を持つ文字列を探し出したい」場面です。
よくあるケースとして、メールアドレスや電話番号の入力チェックがあります。ユーザーがフォームに入力した内容が正しい形式かどうか確かめるとき、正規表現を使えばパターンを指定するだけで照合できます。
他にも、文章の中から数字だけを取り出したい場面や、テキストファイル・CSVデータの中から必要な情報を抜き出したい場面など、活躍の幅は広いです。
出力結果
['neko@example.com', 'inu@sample.jp']文章の中に含まれるメールアドレスに見える文字列を取り出す例です。
エンジニアの実務で使われる理由
エンジニアの日常業務では、大量のテキストデータを扱う場面が頻繁に訪れます。
サーバーのログファイルから特定のエラー文字列を探したり、フォームの入力内容が正しい形式かどうかを確認したりといった作業は、その代表例です。こうした処理を1件ずつ手作業で対応するのは、現実的ではありません。
正規表現を使えば、数十万行のデータから条件に合う部分だけを自動で取り出せます。
「書いたら終わり」ではなく「書いた後も何度でも使い回せる」のが、正規表現が実務で重宝される理由です。
出力結果
['ネコの接続に失敗']ログの各行からエラーメッセージを取り出す例です。「ERROR:」のあとに続くテキストを抽出しています。
正規表現を使う準備
正規表現を使い始めるのに、難しい準備はありません。Pythonをインストールした環境であれば、すぐに利用できます。
Pythonには正規表現を扱うためのreモジュールが標準で備わっており、コードの先頭に1行書くだけですぐ使えます。
ここからは実際に、正規表現の関数や使い方について見ていきましょう。
reモジュールの読み込み
reとはRegular Expression(正規表現)の略で、Pythonで正規表現を使うための機能がまとまったモジュールです。
re.search()やre.findall()といった関数は、すべてreの中に入っています。
reはPythonの標準ライブラリに含まれているため、追加のインストールは必要ありません。コードの先頭にimport reと書くだけで、正規表現に関するさまざまな機能が使えます。
その機能とは、reの関数です。reの関数は基本的に
re.関数名(r"パターン", 対象文字列)という形で使います。
例えばre.search(r"ネコ", text)であれば、「textの中からネコというパターンを探してください」という意味です。
パターンの前にrをつけているのは、raw文字列と呼ばれます。正規表現を正しく動かすために必要な書き方です。
出力結果
<re.Match object; span=(0, 2), match='ネコ'>文字列の中に「ネコ」という文字が含まれているかを調べている例です。re.search(r"ネコ", text)で、textの中から「ネコ」というパターンを探しています。
一致した場合はMatchオブジェクトが返ります。Matchオブジェクトとは、検索結果をまとめて保持しているものです。一致した文字列そのものだけでなく、「文字列のどこで一致したか」という位置情報も一緒に持っています。
出力のspan=(0, 2)が「0文字目から2文字目で一致した」という位置、match='ネコ'が一致した文字列です。一致しなかった場合はNoneが返ります。
ちなみに、Matchオブジェクトから一致した文字列だけを取り出したいときは、.group()を使います。print(result)だと<re.Match object ...>という形式で表示されますが、print(result.group())とすると、シンプルにネコと表示されます。
matchとsearchの違い
re.match()とre.search()はどちらもパターンが一致するかを調べる関数ですが、探す範囲が違います。
match()は文字列の先頭からしか調べません。先頭にパターンがない場合はNoneが返ります。一方、search()は文字列全体を対象にして、最初に一致した部分を探します。
同じ文字列と同じパターンに対して、match()とsearch()の結果がどう変わるかを確認してみましょう。
出力結果
None
<re.Match object; span=(4, 6), match='ネコ'>「文字列のどこかにパターンがあれば見つけたい」という場面がほとんどなので、迷ったらsearch()を使うのが無難です。
findallで一致した文字をまとめて抽出
re.findall()は、パターンに一致するすべての文字列をまとめてリストとして返す関数です。search()が最初の1件だけを返すのに対し、findall()は条件に合う部分をすべて取り出してくれます。
一度に複数の結果を取得できるため、データの中から特定の形式の文字列を一括で抽出したい場合に便利です。結果はリスト形式で返ってくるため、その後の処理にも組み込みやすい形になっています。
下記は、文章の中に含まれる数字をすべて取り出す例です。パターンに一致する部分がリストになって返ってきます。
出力結果
['2', '1', '3']r"\d+"は「数字が1文字以上続く」というパターンです。
\dが「0〜9のうちの1文字」、+が「1文字以上繰り返す」を意味しており、2つを組み合わせて「数字が1文字以上続く部分」を表しています。
re.findall()の結果が数値ではなく文字列のリストとして返ってきているのが確認できますね。数値として使いたいならint()で変換しましょう。
compileで正規表現を再利用する方法
同じパターンを何度も使う場合は、re.compile()であらかじめパターンをオブジェクトとして作っておくのがおすすめです。毎回パターン文字列を書かずに済むため、コードがすっきりします。
compile()で作ったオブジェクトは、search()やfindall()などのメソッドをそのまま持っており、通常の書き方と同じように使えます。
出力結果
['3']
['1']
['10']正規表現の基本パターン
正規表現では、数字・空白・改行といったよく使われる文字の種類を、記号で表せるようになっています。
最初は「この記号は何を意味するんだろう?」と戸惑うかもしれません。ここでは実用的なパターンを取り上げ、具体的なコード例とともに説明していきます。
数字を表すパターン
数字を指定したいなら\dまたは[0-9]を使います。どちらも「0から9のうちの1文字」を意味します。
\d{3}のように{}で個数を指定すると、「数字が3文字続く」といった条件を表せます。郵便番号や電話番号など、桁数が決まっている数値を扱う場面では特に有用です。
出力結果
['123-4567', '987-6543']住所データから郵便番号を抽出する例です。
re.findall()は、パターンに一致するすべての文字列をまとめてリストとして返す関数でしたね。\d{3}-\d{4}で「3桁・ハイフン・4桁」のパターンを指定しています。
文字数や繰り返しの指定
正規表現では、パターンが何回続くかを*、+、?、{}で指定できます。各記号の意味を確認しておきましょう。
記号 |
意味 |
|---|---|
* |
0回以上の繰り返し |
+ |
1回以上の繰り返し |
? |
0回または1回 |
{n} |
ちょうどn回 |
{n,m} |
n回以上m回以下 |
例えばa+は「aが1文字以上続く」、a{2,4}は「aが2〜4文字続く」という意味になります。繰り返しの回数を細かく指定したい場面で使う記法です。
出力結果
['ニャ', 'ニャ', 'ニャ', 'ニャ', 'ニャ', 'ニャ']上記の例では、繰り返し文字を取り出しているのが確認できますね。
改行や空白を含む文字列の扱い
テキストデータに改行や空白が含まれるのは当然のことです。正規表現ではこうした空白文字も記号で表します。
使われる記号を下記の表にまとめました。ここで、しっかり押さえておきましょう。
記号 |
意味 |
|---|---|
\n |
改行 |
\t |
タブ |
\s |
空白・タブ・改行を含む空白文字全般 |
ちなみに、「.(任意の1文字)」はデフォルトでは改行に一致しません。複数行にまたがるテキストを扱う際は、re.DOTALLというオプションを追加すると、「.」が改行にも一致するようになります。
出力結果
ネコ:おはよう
イヌ複数行テキストから改行をはさんだ部分を取り出す例です。re.DOTALLを使うと、改行を含む範囲も一致します。
処理の流れを確認しておきましょう。
- re.search()がtextの中からr"ネコ.+イヌ"に一致する部分を探す
- re.DOTALLを渡しているため、改行も含めて探す
- 「ネコ:おはよう\nイヌ」の部分が一致し、Matchオブジェクトがresultに返る
- result.group()で一致した文字列だけを取り出し、画面に表示される
result.group()はre.search()で一致した文字列を取り出すためのメソッドです。
re.search()はMatchオブジェクトを返しますが、そのままprint(result)とすると<re.Match object; span=(0, 8), match='ネコ:おはよう\nイヌ'>のような表示になり、一致した文字列だけを見たい場合には少し冗長な出力になります。
そんなときは.group()を使って、一致した文字列だけをシンプルに取り出しましょう。
エスケープが必要な文字
正規表現では、.、?、*、+、()、[]、{}、^、$、\、|といった文字は特別な意味を持っています。これらの文字そのものを検索したい際には、前に\をつけてエスケープする必要があります。
例えば、ピリオド(.)は正規表現では「任意の1文字」を意味するため、文中のピリオドを検索したいときは\.と書かないと、意図しない文字まで一致してしまうので注意しましょう。
出力結果
3上記は、文章の中のピリオド(.)の数を数えるコード例です。\.でピリオドをそのままの文字として指定しています。
正規表現の実践的な使い方
基本的なパターンを覚えたら、実際のデータ処理に応用してみましょう。文字列の抽出・置換・条件分岐など、よく使われる処理のパターンを順に確認していきます。
コード例を参考に、手元で動かしながら読むと理解が深まるはずです。
条件に合う文字列の抽出
re.findall()と組み合わせて()(グループ)を使うと、パターン全体ではなく特定の部分だけを取り出せます。()で囲った部分だけが抽出対象になります。
re.search()で一致した要素は.group()で取り出せます。group(1)のように番号を指定すると、複数のグループのうち特定のものだけを取り出すことも可能です。
出力結果
['3', '1', '5'][\u30A0-\u30FF]+はカタカナが1文字以上続く部分を表します。:(\d+)は「:」に続く数字を表し、()で囲んだ部分だけが抽出対象になっています。
subを使った文字列の置換
re.sub()を使うと、パターンに一致した部分を別の文字列に置き換えられます。書き方は
re.sub(r"パターン", 置換後の文字列, 対象文字列)
です。
個人情報にあたる数字を伏せ字にしたり、表記揺れを統一したりする場面で役立ちます。
次のコードは文章中の数字をすべて「*」に置き換える例です。
出力結果
ネコの年齢は*歳、体重は*キログラムです。数字部分が伏せ字になっていることがわかりますね。
if文と組み合わせた判定
re.search()やre.match()の結果をif文で判定すると、入力内容が正しい形式かどうかをチェックできます。
一致した場合はMatchオブジェクト(真)、一致しなかった場合はNone(偽)が返ります。PythonではNoneは偽として扱われるため、if文の条件としてそのまま使えます。
例えば、フォームへの入力値を確認する処理や、特定のパターンを持つ行だけを選別するときに有用です。
入力された文字列が郵便番号の形式かどうかを判定するコード例を見てみましょう。
出力結果
ネコでも使える正しい郵便番号の形式です。^は文字列の先頭、$は文字列の末尾を表しています。これにより「この形式にぴったり一致する」という指定ができるのです。
ちなみに、^と$をつけないと「123-4567abc」のような文字列も通過してしまいます。
【関連】Pythonのif文の使い方は?基本構文から応用まで初心者向けに徹底解説
正規表現チェッカーを使う際の注意
正規表現の動作確認には、ブラウザ上で動かせる正規表現チェッカーを使うと便利です。パターンを入力するとリアルタイムで一致する部分が確認でき、書き方の学習にも役立ちます。
ただし、チェッカーとPythonのreモジュールの仕様が完全に同じとは限りません。文字クラスの扱いやフラグの指定方法など、細かい点で差が出る場合があるためです。
チェッカーで確認した後は、必ずPythonのコード上でも動作を確かめるようにしてください。
下記は、チェッカーで確認したパターンをPythonコードで検証する例です。
出力結果
000-0000 -> True
イヌ123 -> False
456-7890 -> Trueつまずきやすいポイントと解決のヒント
いざ正規表現を使い始めると、「思ったより広い範囲が一致してしまった」「バックスラッシュの扱いがよくわからない」といった壁にぶつかることがあります。
つまずきやすい点をあらかじめ知っておくと、問題が起きた際に原因を特定しやすくなるでしょう。ここでは、初心者が陥りがちな点にフォーカスして解説します。
欲しい範囲より広く一致してしまう場合
.*や.+を使うと、想定よりも広い範囲に一致してしまうケースがあります。これは*や+が「できるだけ多く一致しようとする」性質を持っているためで、この性質を「貪欲マッチ」と呼びます。
例えば[.+]というパターンで「角括弧の中身だけを取りたい」と思っても、コード内容によっては最初の[から最後の]まで複数の角括弧をまたいで一致してしまうのです。
そんなときは+?のように?をつけると「できるだけ短い範囲で一致する」非貪欲マッチになります。
出力結果
['[かわいい]とイヌ[元気]']
['[かわいい]', '[元気]']raw文字列を使う理由
Pythonでは\n(改行)や\t(タブ)のように、バックスラッシュを使ったエスケープ記法があります。
正規表現でも\dや\sのようにバックスラッシュをよく使うため、通常の文字列として書くとPythonが「これはエスケープ記法だ」と解釈してしまい、意図しない動作になってしまいます。
これを防ぐために、正規表現のパターンはr"……"の形のraw文字列で書くのが一般的です。raw文字列では\がそのままバックスラッシュとして扱われるため、正規表現のパターンを意図通りに書けます。
この例では結果は同じですが、\b(単語境界)などのパターンではraw文字列を使わないと正しく動作しない場合があります。raw文字列を使う習慣をつけておきましょう。
出力結果
['3']
['3']読みにくい正規表現を避ける工夫
正規表現は短く書ける反面、複雑になると後から見返した際、「これは何を検索しているんだろう?」と意味が取りにくくなります。
特に慣れないうちは、1行に詰め込みすぎずにパターンを変数に分けて名前をつけておくのがおすすめです。
パターンをいくつかの変数に分けて名前をつけておくと、後から見返しても意味を理解しやすくなります。
出力結果
['123-4567']長いパターンを分割して管理する方法
正規表現のパターンが長くなると、一行で書くと読みづらくなります。冗長になってきたら、re.VERBOSEフラグを使うのも1つの手です。
re.VERBOSEを使うと、パターンの中の空白や改行が無視されるようになり、#でコメントも書き込めます。意味のかたまりごとに分けられるため、長いパターンでも読みやすい書き方です。
出力結果
['03-1234-5678']よくある質問(Q&A)
Q. 正規表現が使えるのは英語の文字列だけですか?
日本語(ひらがな・カタカナ・漢字)にも使えます。ただし、文字クラスで日本語を指定するには[\u3040-\u309F](ひらがな)のようにUnicodeの範囲を指定する方法が取られるため、英語とは少し書き方が変わります。
Q. matchとsearchはどちらを使えばよいですか?
文字列の先頭にのみパターンがある前提で検索するならmatch()、文字列のどこかにパターンがあれば見つけたいならsearch()を使います。迷った場合はsearch()を使う方が意図しない見逃しを防げるでしょう。
Q. findallで何も返ってこないのはなぜですか?
パターンが文字列と一致していない可能性があります。まずはパターンをできるだけ単純にして試し、少しずつ条件を加えながら原因を絞っていきましょう。raw文字列(r"...")を使い忘れていないかも確認してみてください。
Q. re.subで一部だけ変えたいときはどうしますか?
グループ(())でキャプチャした部分を、置換後の文字列に\1などで参照できます。例えばre.sub(r"(\d+)匹", r"\1頭", text)のようにすると、数字の部分はそのまま残しつつ「匹」だけを「頭」に変えられます。
Q. 正規表現を覚えるコツはありますか?
最初からすべての記法を覚えようとしなくて大丈夫です。\d(数字)・\s(空白)・+(1回以上)の3つから始めて、実際のデータで試しながら少しずつ覚えていきましょう。
まとめ
この記事では、Pythonの正規表現について、基本的な考え方から実践的な使い方まで順を追って解説してきました。
正規表現が役立つのは、次のような場面です。
正規表現が活躍する場面
- メールアドレスや電話番号など、特定の形式に合う文字列を検索、検証したいとき
- テキストやCSVデータの中から必要な情報を一括で取り出したいとき
- ログファイルの解析や、文字列の一括置換など、データ整形の場面
押さえておきたいポイントは、次の通りです。
重要なポイント
- Pythonの正規表現はimport reだけで使用でき、インストールは不要
- re.search()は文字列全体を対象に検索し、re.findall()は一致した部分をすべてリストで返す
- 正規表現のパターンはraw文字列(r"……")で記述するのが一般的
- や+はデフォルトで貪欲マッチになるため、範囲を絞りたい場合は+?や?を使う
- パターンが長くなったら変数やre.VERBOSEで分割して読みやすくする
正規表現はPython以外のプログラミング言語はもちろん、ログ調査やデータベースでの条件検索など、あらゆるIT分野でも広く使われているため、ここで覚えた知識がさまざまな場面で役立つのは間違いありません。
最初は記号の意味がつかみにくく感じるかもしれませんが、よく使うパターンから少しずつ試していけば、自然と使いこなせるようになっていきます。
まずは手元のテキストデータから、遊び感覚で小さく試すことから始めてみてください。