R

ReflexKR

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

時間 0:50–1:40

ひき逃げ車両:文字クラス

CASE-002

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

依頼書

ひき逃げ車両

依頼人

管轄警察署のカン班長

証拠品

目撃者の証言1枚と、地下駐車場の入出庫記録。目撃者はナンバープレートを一部しか見ていない。

解決条件

駐車記録からナンバーをすべて抜き出し、目撃証言に合う車両を1台に絞り込む。

夕暮れどき、カン班長が濡れた傘を振りながら事務所の扉を押し開けます。「昨夜の22時45分ごろ、地下駐車場の出口でひき逃げだ。目撃者はナンバーを全部は見てないそうでな」所長はキーボードから手を離さずに答えます。「全部見てないなら、見たぶんだけパターンに書けばいい。証拠はパターンを残す」

目撃者の証言はこうです(ソウルの事件なので韓国語のままです)。

- 앞 두 자리는 12 아니면 17이었어요.
- 가운데 한글은 가, 거, 고 중 하나였어요.
- 마지막 숫자는 7이었어요.
- 차는 출구(OUT) 쪽으로 빠져나갔어요.

訳すとこうなります。「最初の2桁は12か17でした」「真ん中のハングルは가、거、고のどれかでした」「最後の数字は7でした」「車は出口(OUT)のほうへ出ていきました」。韓国のナンバープレートは「数字2桁+ハングル1文字+数字4桁」という形をしています。

駐車場の入出庫記録です。カメラはときどきナンバーを読み間違えます。

2026-10-19 22:41 IN  B1 12가3457
2026-10-19 22:43 IN  B1 34나1207
2026-10-19 22:47 OUT B2 17거3407
2026-10-19 22:52 IN  B1 12-3457
2026-10-19 22:58 OUT B2 56다9081
2026-10-19 23:05 IN  B1 12a3457

証言の1行1行が「この位置にはこの中のどれか」という条件になっています。今日学ぶ 文字クラス こそ、その条件を書くための文法です。

2-1. 角かっこ:この中の1文字 (5分)

角かっこ [...] は 中に書いた文字のうちどれか1文字 を表します。目撃者が「가、거、고のどれか」と言ったので、その位置は [가거고] と書きます。

import re

re.findall("12[가거고]3457", "12가3457 12거3457 12나3457")   # ['12가3457', '12거3457']
re.findall(r"[ab]c", "ac bc cc")                            # ['ac', 'bc']
re.search(r"[abc]", "xyz")                                  # None
  • 角かっこの中に文字がいくつあっても、使うのは 1文字分 だけです。[가거고] は3文字ではなく1文字です。
  • 順番は関係ありません。[가거고] と [고거가] は同じクラスです。
  • 1限目の . が「任意の1文字」だったのに対し、角かっこは「選んだ文字のうちの1文字」です。

証拠分析室

re.search(r"", text)

戻り値

真ん中の文字を [가거고] に変えてみましょう。가、거、고は通り、나ははじかれるはずです。

2-2. 範囲:[0-9]、[a-z]、[가-힣] (8分)

数字10個を [0123456789] のように全部書くのは面倒です。角かっこの中で2つの文字のあいだに - を入れると 範囲 になります。[0-9] は0から9まで、[a-z] は英小文字すべてです。

re.findall(r"[0-9]", "B1")            # ['1']
re.findall(r"[a-z0-9]", "B1 b2")      # ['1', 'b', '2']
re.findall(r"[a-zA-Z]", "a1Z")        # ['a', 'Z']
  • 範囲はいくつも並べて書けます。[a-z0-9] は小文字または数字の1文字です。
  • 範囲の基準は文字の コードポイント(Unicodeの番号)の順番です。だから [a-z] に大文字は入りません。

ハングルの完成形の文字は、Unicodeで 가(U+AC00)から 힣(U+D7A3)までの11,172文字が順番に並んでいます。そのため [가-힣] ひとつで、完成したハングル1文字をすべて捕まえられます。

print(hex(ord("가")), hex(ord("힣")))   # 0xac00 0xd7a3
re.findall(r"[가-힣]", "ㄱ ㅏ 가")       # ['가']

ㄱ、ㅏ のような単独の字母(子音・母音のパーツ)は別の区画にあるので、[가-힣] には入りません。ナンバーの真ん中の文字はいつも完成形なので、この範囲で十分です。

範囲を逆向きに書くと、パターンを作った時点でエラーになります。

re.compile("[a-Z]")   # re.PatternError: bad character range a-Z at position 1

証拠分析室

re.search(r"", text)

戻り値

真ん中の「가」を [가-힣] に変えて、ナンバー4つをすべて見つけてみましょう。カメラが読み間違えた2行ははじかれるはずです。

ドットのひとこと

範囲はコードポイントの小さいほうから大きいほうへしか書けないんです。大文字も小文字もまとめて捕まえようと [a-Z] と書くとエラーになりますよ。aは97番、Zは90番で、逆向きですからね。両方ほしいなら [a-zA-Z] みたいに範囲を2つ書いてください。

2-3. 否定:[^...] これ以外 (7分)

角かっこのすぐ内側、先頭に ^ を書くと意味がひっくり返ります。[^0-9] は 数字ではない1文字 です。

re.findall(r"[^0-9]", "12가34")     # ['가']
re.findall(r"[0-9^]", "2^3")        # ['2', '^', '3']
re.findall(r"[^0-9]", "1\n2")       # ['\n']
  • ^ が否定になるのは 先頭にあるときだけ です。[0-9^] のように後ろにあれば、ただの ^ という文字です。
  • [^0-9] もやはり 1文字分を使います。「数字がない場所」ではなく「数字ではない文字1つ」です。
  • 改行も数字ではないので、[^0-9] に引っかかります。

駐車場のカメラは、ときどき数字を英字と読み間違えます。1 を I に、5 を S に、2 を Z に、といった具合です。数字でもハングルでも空白でもない文字を探せば、読み間違えた箇所が浮かび上がります。

ドットのひとこと

ぼくは窓(改行)を越えられないけど、[^0-9] は越えちゃいます。「数字ではない文字」には改行も入るからです。複数行の記録で [^...] を使うときは、改行まで捕まえていないか必ず確かめてくださいね。

証拠分析室

re.search(r"", text)

戻り値

[^0-9] はハングルや空白まで捕まえてしまいます。角かっこの中に 가-힣 と半角スペース1つを足して、読み間違えた文字だけを残してみましょう。

2-4. 省略記法:\d \w \s (10分)

よく使うクラスは、バックスラッシュと英字1文字で短く書けます。大文字にすると意味が逆になります。

記法意味だいたい同じクラス
\d数字1文字[0-9]
\D数字ではない1文字[^0-9]
\w文字・数字・アンダースコアの1文字(ハングルや日本語も含む)[a-zA-Z0-9_] より広い
\W\w ではない1文字
\s空白類の1文字(スペース、タブ、改行など)[ \t\n\r\f\v] より広い
\S空白類ではない1文字
re.findall(r"\d", "B1 12가")       # ['1', '1', '2']
re.findall(r"\D", "12가-3")        # ['가', '-']
re.findall(r"\w", "a_1 가!")       # ['a', '_', '1', '가']
re.findall(r"\W", "a_1 가!")       # [' ', '!']
re.findall(r"\s", "a b\tc\nd")     # [' ', '\t', '\n']
re.findall(r"\S", "a b\tc")        # ['a', 'b', 'c']

省略記法は、角かっこの中でほかの文字と混ぜて使えます。[\d,.] は「数字かカンマかピリオド」の1文字です。

re.findall(r"\d[\d,.]\d", "1,2 3.4 5-6 789")   # ['1,2', '3.4', '789']

では、ナンバーを省略記法で書いてみましょう。真ん中を \D(数字ではないもの)にすると楽そうに見えますが、カメラが読み間違えた 12-3457 や 12a3457 まで通ってしまいます。真ん中は「数字ではない何か」ではなく「ハングル1文字」でなければなりません。

証拠分析室

re.search(r"", text)

戻り値

\D はハイフンも英字も受け入れてしまいます。真ん中の \D を [가-힣] に変えて、本物のナンバーだけを残してみましょう。

2-5. 角かっこの中ではルールが変わる (5分)

角かっこの中では、ほとんどのメタ文字が力を失います。[.*+?] はピリオド、アスタリスク、プラス、クエスチョンマークのどれか1文字にすぎません。バックスラッシュを付ける必要もありません。

角かっこの中でも特別なまま残るのは、次の4文字だけです。

  • \ :エスケープと、\d のような省略記法
  • ] :クラスの終わり。ただし 先頭 にあれば文字そのもの
  • ^ :先頭 にあれば否定
  • - :範囲。ただし 先頭か末尾 にあれば文字そのもの
re.findall(r"[.*+?]", "a.b*c+d?")    # ['.', '*', '+', '?']
re.findall(r"[]a]", "a]b")           # ['a', ']']
re.findall(r"[-+]", "3-2+1")         # ['-', '+']
re.findall(r"[a\-z]", "a-mz")        # ['a', '-', 'z']
re.findall(r"[\]\\]", r"a]b\c")      # [']', '\\']

[a\-z] は範囲ではなく、a、-、z の3文字です。だから m は捕まりません。位置のルールで迷ったら、素直にバックスラッシュを付けたほうが読みやすくなります。

2-6. Unicode、そして避けるべき重ね記号 (5分)

Pythonのstrパターンは、デフォルトでUnicodeを扱います。そのため省略記法は思ったより広い範囲を捕まえます。\w がハングルを捕まえるのはすでに見ました。\d はアラビア・インド数字の ٣(U+0663、数字の3)も数字とみなします。\s は、日本語の文書でもおなじみの全角スペース(U+3000)も捕まえます。

re.findall(r"\d", "3٣")          # ['3', '٣']
print(int("٣"))                  # 3
re.findall(r"\s", "a\u3000b")    # ['\u3000']
  • ちょうど0〜9だけがほしいなら、\d の代わりに [0-9] を使います。
  • 省略記法をASCIIの範囲に絞る re.ASCII フラグは11限目で学びます。

最後にもうひとつ。角かっこの中で [[、--、&&、~~、|| のように同じ記号を重ねて書くと、FutureWarningが出ます。Pythonが将来、クラス同士の集合演算(和集合、積集合など)に使うためにとってある書き方だからです。

import warnings
warnings.simplefilter("always")

re.compile(r"[[a]")      # FutureWarning: Possible nested set at position 1
re.compile(r"[a&&b]")    # FutureWarning: Possible set intersection at position 2

今は動きますが、将来は意味が変わるかもしれません。そうした文字がどうしても必要なら、[\[a]、[a&\&b] のようにどちらか一方をエスケープします。12限目でもう一度整理します。

事件解決 (10分)

駐車記録が parking という文字列に入っているとしましょう。まず本物のナンバーだけをすべて抜き出します。カメラが読み間違えた2行は外れます。

plates = re.findall(r"\d\d[가-힣]\d\d\d\d", parking)
print(plates)   # ['12가3457', '34나1207', '17거3407', '56다9081']

次に、目撃証言を1桁ずつクラスに置き換えます。最初の2桁は 1[27]、真ん中は [가거고]、最後は 7 です。

print(re.findall(r"1[27][가거고]\d\d\d7", parking))         # ['12가3457', '17거3407']
print(re.findall(r"OUT B\d 1[27][가거고]\d\d\d7", parking))  # ['OUT B2 17거3407']

候補は2台ですが、12가3457 は事故の前に入ったきり、出た記録がありません。事故の2分後に出口から出ていった車は 17거3407 の1台だけです。

事件解決報告書 — 22:47にB2出口から出た17거3407が、目撃証言とすべて一致。カン班長に車両照会を依頼した。

カン班長が帰ると、所長が画面のパターンを指でなぞります。「\d\d[가-힣]\d\d\d\d。同じものを4回も書いてる。それに最近のナンバーは、頭が3桁のものもある」次の回では、この長いパターンを短くする方法を学びます。

練習問題

  1. 駐車記録から B1、B2 のような出入口の名前だけを探すパターンを書いてみましょう。B[12] と B\d はどこが違うでしょうか。
  2. [^\d\s] は何を見つけるでしょうか。上のカメラ記録 12가3457 I2가3457 12가34S7 1Z거3407 で、まず予想してから実行してみましょう。
  3. 時刻 22:41 のような「2桁:2桁」の形を探すパターンを、省略記法で書いてみましょう。

所長の手帳

  • [abc] は a、b、c のどれか1文字。何文字入れても1文字分しか使わない。

  • [a-z0-9] のように、範囲はコードポイント順。ハングルの完成形は [가-힣]。[a-Z] はエラー。

  • [^...] は先頭の ^ のときだけ否定。改行も捕まえる。

  • \d \w \s と、その逆の \D \W \S。[\d,.] のようにクラスの中に混ぜて使える。

  • クラスの中では . * + ? は文字そのもの。特別なのは \ ] ^ - の4つだけ。

  • strパターンの \d \w \s はUnicode全体が対象。0〜9だけなら [0-9]。

← 前へ

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

次へ →

3限目 · 破れた電話帳:量指定子

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

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

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

質問と回答

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

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

R

ReflexKR

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

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

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

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

Built with Reflex