R

ReflexKR

Reflex公式ドキュメント
目で見て覚える正規表現
1限目

時間 0:00–0:50

消えた決済記録:文字をそのまま探す

CASE-001

グレップ・アンド・コー データ探偵事務所

依頼書

消えた決済記録

依頼人

PCバン「レベルアップ」の店長

証拠品

決済サーバーのログ。客の一人が4.99ドルを払ったのに記録がないと苦情を言っている。

解決条件

ログからエラーが出た行を見つけ、ちょうど$4.99の決済だけを選び出す。

初出勤の朝、所長がコーヒーカップを置き、紙を1枚こちらへ滑らせます。「昨日、PCバン(韓国のネットカフェ)の店長が来てね。客は4.99ドル払ったと言うのに、記録がないそうだ。ログは嘘をつかない。探して」

ログはこんな形をしています。今日はこの5行から証拠を探します。

2026-10-20 09:12:01 INFO  PAY user=kim  amount=$4.99  status=OK
2026-10-20 09:12:07 INFO  PAY user=lee  amount=$14.99 status=OK
2026-10-20 09:13:44 ERROR PAY user=park amount=$4.99  status=TIMEOUT
2026-10-20 09:15:02 INFO  PAY user=choi amount=$4999  status=OK
2026-10-20 09:16:30 ERROR DB  connection lost

1-1. 正規表現は「文字のパターン」 (5分)

正規表現(regular expression)は、探したい文字の 形 を書き表す小さな言語です。Pythonでは re モジュールで使います。いちばん簡単な正規表現は、ただの文字です。ERROR というパターンは ERROR という文字を探します。

import re

log = "2026-10-20 09:13:44 ERROR PAY user=park"
m = re.search("ERROR", log)
print(m)          # <re.Match object; span=(20, 25), match='ERROR'>
  • re.search(パターン, 文字列) は、文字列の 先頭から 見ていき、パターンが最初に現れる場所を探します。
  • 見つかれば Matchオブジェクト を、見つからなければ None を返します。

証拠分析室

re.search(r"", text)

戻り値

searchとfindallを切り替えてみましょう。searchは最初のひとつだけ、findallはすべてを見つけます。

1-2. 見つかったか、どこで見つかったか:Matchオブジェクト (5分)

Matchオブジェクトは「見つけた」という証拠であり、何をどこで見つけたかが書かれた報告書でもあります。

m = re.search("ERROR", log)
if m:                 # 見つかれば真、Noneなら偽
    print(m.group())  # ERROR       (見つけた文字)
    print(m.span())   # (20, 25)    (開始位置, 終了位置)
  • if m: のように、そのまま真偽で判定します。見つからないと None なので、m.group() を呼ぶと AttributeError になります。
  • span() の終了位置は 含みません。log[20:25] が "ERROR" になるのと同じルールです。
  • 証拠分析室の「最初のマッチ」欄には、これらの値がそのまま表示されます。

1-3. 特別な14文字 (10分)

ほとんどの文字は自分自身を探します。ところが、下の14文字は正規表現の中で 特別な意味 を持つメタ文字です。

.  ^  $  *  +  ?  {  }  [  ]  \  |  (  )

今日はその最初のひとつ、. だけを学びます。. は 改行以外の任意の1文字 を表します。残りはこれから1回にひとつずつ取り出して使っていきます。

re.search("a.c", "abc")    # マッチ:b が . の位置に入る
re.search("a.c", "a-c")    # マッチ:- も「任意の文字」
re.search("a.c", "ac")     # None:. は必ず1文字分を使う
re.search("a.c", "a\nc")   # None:改行は . にならない

ドットのひとこと

ぼくは事務所の机にも椅子にも、所長のキーボードの上にだって、どこにでも登れます。でもたったひとつ、窓(改行)だけは越えられないんです。だから名前がドット(.)。いつか誰かが窓を開けてくれたら、そのときは越えられるらしいですよ。

証拠分析室

re.search(r"", text)

戻り値

a.c は、aとcのあいだにちょうど1文字あるときだけマッチします。

1-4. バックスラッシュで特別な意味を消す (10分)

では事件に戻りましょう。いちばん上のログ5行が pay_log という文字列に入っているとします。4.99ドルの決済を探そうとして 4.99 と書くと、どうなるでしょうか。

re.findall("4.99", pay_log)   # ['4.99', '4.99', '4.99', '4999']  ← 4つ?

2つのはずの結果が4つあります。$14.99 の後ろ半分の 4.99 が引っかかり、さらに . が「任意の1文字」なので $4999 の 4999 まで入ってきました。メタ文字を 文字そのまま 探したいときは、前にバックスラッシュ \ を付けます。\. は本物のピリオド1つです。

$ もメタ文字です。意味は4限目で学びますが、今は「そのまま書いてもドル記号は探してくれない」とだけ覚えておいてください。というわけで、正解は \$4\.99 です。

re.findall(r"\$4\.99", pay_log)   # ['$4.99', '$4.99']

証拠分析室

re.search(r"", text)

戻り値

4.99 から始めて、\. と \$ を順に入れてみましょう。解決条件がすべて緑色になればOKです。

1-5. なぜ r"..." と書くのか:raw文字列 (5分)

上では r"\$4\.99" のように、引用符の前に r を付けました。これを raw文字列 と呼びます。

バックスラッシュはPythonの文字列の中でも特別です。"\n" は改行、"\t" はタブでしたね。そのため正規表現を普通の文字列で書くと、Pythonが先にバックスラッシュを解釈してしまい、re にはおかしなパターンが渡ります。

print(len("\b"))    # 1  ← Pythonが \b を「バックスペース」1文字に変えた
print(len(r"\b"))   # 2  ← バックスラッシュとb、2文字のまま
  • r"..." はバックスラッシュに手を付けず、そのまま re に渡します。正規表現は必ずraw文字列で書きましょう。
  • Python 3.12からは、"\d" のようにPythonが知らないエスケープを普通の文字列に書くとSyntaxWarningが出ます。raw文字列なら警告は出ません。

1-6. 客が入力した文字をそのまま探す:re.escape (5分)

PCバンの店長が「客のIDが kim.99+ なんだけど、これも探してくれ」と言います。こういう文字をそのままパターンに書くと、. と + がメタ文字として解釈されてしまいます。人が入力した文字をそのまま探すときは、re.escape がメタ文字の前にバックスラッシュを付けてくれます。

print(re.escape("kim.99+"))                        # kim\.99\+
re.search(re.escape("kim.99+"), "user=kim.99+")    # マッチ

事件解決 (10分)

まとめると、エラーが出た行は ERROR で、ちょうど4.99ドルの決済は \$4\.99 で見つかります。2つの条件を重ねると、客 park の決済が TIMEOUT エラーで記録されていなかったことがわかります。

for line in pay_log.splitlines():
    if re.search("ERROR", line) and re.search(r"\$4\.99", line):
        print(line)
# 2026-10-20 09:13:44 ERROR PAY user=park amount=$4.99  status=TIMEOUT

事件解決報告書 — 決済サーバーは09:13:44にタイムアウトで決済を失敗扱いにしたが、客の画面には完了と表示されていた。PCバンの店長に返金を勧めた。

練習問題

  1. ログから INFO の行をすべて探してみましょう。いくつありますか。
  2. \$1.\.99 は何を見つけるでしょうか。分析室で確かめる前に、まず予想してみてください。
  3. re.escape("1+1=2? [yes]") の結果を予想してから、実際に実行してみましょう。

所長の手帳

  • ほとんどの文字は自分自身を探す。特別なのはメタ文字14個 . ^ $ * + ? { } [ ] \ | ( ) だけ。

  • . は改行以外の任意の1文字。本物のピリオドは \.

  • re.search は Match か None を返す。if m: で判定し、m.group()、m.span() で中身を見る。

  • 正規表現は必ず r"..." のraw文字列で。

  • 人が入力した文字をそのまま探すときは re.escape。

← 前へ

講座の概要

次へ →

2限目 · ひき逃げ車両:文字クラス

新しい授業・講座のお知らせを受け取る

この講座に授業が追加されたり、続きの講座が公開されたりしたらメールでお知らせします。

収集項目:メールアドレス・目的:この講座の新しい授業・公開のお知らせ・保有期間:解除のご依頼まで。他の目的には使わず、第三者にも提供しません。お知らせメールに返信いただければすぐに削除します。

質問と回答

この回について気になることを書き込んでください。すべての言語のページの質問がここに集まります。

リンクや書式なしで、そのままの文字で表示されます。

R

ReflexKR

触って学ぶPythonとReflex。すべての例はReflex 0.10.0で実際に動かして確認しています。

ReflexKRはバナー広告なし・無料で運営しています。役に立ったら、コーヒー一杯で応援してください。 ☕ コーヒーで応援する

アフィリエイトと支援について
한국어English日本語简体中文EspañolDeutsch

© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0

Built with Reflex