目次
正規表現(Regular Expression、regex)は文字列のパターンを記述するための書式だ。ログファイルから特定のエラーを抽出する、LLMの出力からJSON部分だけを取り出す、メールアドレスの形式を検証するといった場面で、正規表現は数行のコードで強力な処理を実現する。
基本構文
メタ文字
正規表現では通常の文字に加えて、特殊な意味を持つ「メタ文字」を使う。
| メタ文字 | 意味 |
|---|---|
. | 任意の1文字(改行を除く) |
^ | 行の先頭 |
$ | 行の末尾 |
\d | 数字([0-9]と等価) |
\w | 単語文字([a-zA-Z0-9_]と等価) |
\s | 空白文字(スペース・タブ・改行) |
量指定子
| 量指定子 | 意味 |
|---|---|
* | 0回以上 |
+ | 1回以上 |
? | 0回または1回 |
{n} | ちょうどn回 |
{n,m} | n回以上m回以下 |
.* と .+ の違いは「0回以上」と「1回以上」だ。.* は空文字列にもマッチするが .+ は1文字以上が必須だ。
文字クラス
[aeiou] は a, e, i, o, u のいずれか1文字にマッチする。[^aeiou] は先頭の ^ が否定を意味し、母音以外の文字にマッチする。[a-z] は a から z の範囲を表す。
グループとキャプチャ
() で囲んだ部分はグループを形成し、キャプチャグループとして後から参照できる。
import re
text = "Error: 404 Not Found"
match = re.search(r"Error: (\d+) (.+)", text)
if match:
code = match.group(1) # "404"
msg = match.group(2) # "Not Found"
(?:...) は非キャプチャグループで、グループ化はするが後から参照しない場合に使う。
貪欲マッチと非貪欲マッチ
デフォルトの量指定子は「貪欲(greedy)」で、できるだけ多くの文字にマッチしようとする。
text = "<b>太字</b>と<i>斜体</i>"
re.search(r"<.+>", text).group() # "<b>太字</b>と<i>斜体</i>" — 全体にマッチ
re.search(r"<.+?>", text).group() # "<b>" — 最短マッチ(?を付けると非貪欲)
HTMLやXMLを正規表現でパースすることは一般的に非推奨だが、タグを含む文字列を扱う場合に ? による非貪欲マッチが重要になる。
LLM出力の後処理での実用例
LLMはJSON出力を要求してもマークダウンコードブロックで囲んで返すことがある。正規表現でJSON部分だけを抽出できる。
def extract_json(llm_output: str) -> str:
# ```json ... ``` または ``` ... ``` のパターンに対応
pattern = r"```(?:json)?\s*([\s\S]+?)\s*```"
match = re.search(pattern, llm_output)
if match:
return match.group(1)
# コードブロックがなければ { } の部分を探す
match = re.search(r"\{[\s\S]+\}", llm_output)
return match.group(0) if match else llm_output
先読み(Lookahead)と後読み(Lookbehind)
先読みは「マッチするが消費しない」特殊なグループだ。
# 正の先読み: 「円」の前の数字にマッチ
re.findall(r"\d+(?=円)", "1000円と500円") # ['1000', '500']
# 正の後読み: 「¥」の後の数字にマッチ
re.findall(r"(?<=¥)\d+", "¥1000と¥500") # ['1000', '500']
Pythonでの主なre関数
| 関数 | 用途 |
|---|---|
re.match() | 文字列の先頭からマッチ |
re.search() | 文字列全体から最初のマッチを検索 |
re.findall() | すべてのマッチを文字列リストで返す |
re.sub() | マッチした部分を置換 |
re.compile() | パターンをコンパイルして再利用(高速化) |
頻繁に使うパターンは re.compile() でコンパイルしておくと、繰り返し適用する際のパフォーマンスが向上する。
まとめ
正規表現は習得コストが高く見えるが、基本のメタ文字と量指定子、グループを理解すれば実務の大半のケースに対応できる。.* と .+ の差(0回以上か1回以上か)、貪欲マッチと非貪欲マッチ(* と *?)の区別は特に重要だ。LLM出力の後処理やログ解析では、正規表現による文字列抽出が日常的に必要となる。Python の re モジュールと re.compile() によるパターンの再利用を覚えておくと即戦力として使える。
免責事項 — 掲載情報は執筆時点のものです。料金・機能は変更される場合があります。最新情報は各公式サイトをご確認ください。