IDは文字列全体を検証する
IDが「半角大文字2文字、ハイフン、半角数字4桁」という仕様なら、re.fullmatch()と[A-Z]・[0-9]を組み合わせる。部分一致があっただけでは正しいIDとは言えない。先頭の空白、末尾の改行、全角数字、桁数の違いをそのまま拒否することで、後続のファイル名や検索キーの揺れを減らせる。
ここで決めるのは表記の条件だけである。AB-0000というIDが発行済みか、別の利用者に割り当てられていないかは、台帳やデータベースで確認する必要がある。形式検証と存在確認を分けておくと、入力画面のエラー理由も整理しやすい。
許可・拒否の両方をまとめて確認する
example.py
import re
ID = re.compile(r"[A-Z]{2}-[0-9]{4}")
def valid_id(text):
return isinstance(text, str) and ID.fullmatch(text) is not None
cases = [
("AB-0123", True),
("AB-0000", True),
("ab-0123", False),
(" AB-0123", False),
("AB-0123\n", False),
("AB-01234", False),
("AB-0123", False),
("AB-١٢٣٤", False),
(None, False),
]
for value, expected in cases:
actual = valid_id(value)
assert actual is expected
print(repr(value), actual)
assert re.fullmatch(r"\d+", "0123") is not None
assert re.fullmatch(r"\d+", "0123", flags=re.ASCII) is None
assert re.match(r"^[A-Z]{2}-[0-9]{4}$", "AB-0123\n") is not None
assert not valid_id("AB-0123\n")
実行結果
'AB-0123' True
'AB-0000' True
'ab-0123' False
' AB-0123' False
'AB-0123\n' False
'AB-01234' False
'AB-0123' False
'AB-١٢٣٤' False
None False
fullmatch・数字の範囲・入力型の役割
fullmatch()は最初から最後まで一致した場合にMatchを返し、一致しなければNoneを返す。is not Noneで判定すると意図がはっきりする。文字列でない値はisinstance()でFalseにした。呼び出し側のプログラムの誤りとして扱いたいなら、Noneを黙って拒否する代わりにTypeErrorを送出する設計もある。どちらにするかを関数の契約として決める。
Unicode文字列に対する\dは半角の0〜9だけを意味しない。全角数字やアラビア・インド数字なども対象になる。ASCIIに限定したいなら、[0-9]と書くかre.ASCIIを付けた\dを使う。[0-9]は許可範囲が目で分かるため、短いID仕様には読みやすい。\wも英数字だけではなくアンダースコアなどを含むので、英字だけの位置へ安易に使わない。
^…$で囲めば常に同じ、とは考えない方がよい。Pythonの$は文字列末尾の改行の直前にも一致しうるため、例のre.match()は末尾改行付きIDを受け入れる。MULTILINEなどのフラグを追加すると境界の意味も変わる。入力欄一つを検証する目的ならfullmatch()を直接選ぶ方が仕様に沿う。
正規化と検証を混ぜない
前後の空白を消したり、小文字を大文字に直したり、全角文字を半角へ寄せたりするのは正規化である。便利ではあるが、入力を変更する方針なので検証とは分けて考える。この例では入力された値を変えず、条件に合わなければFalseとする。正規化を行うアプリケーションでは、正規化後のIDを利用者に示してから保存するなど、表示と保存の食い違いを防ぐ。
大文字小文字を無視するre.IGNORECASEも、Unicodeでは単なるASCII大文字・小文字の対応だけに限定されない。仕様が半角英字のみで大小文字を認めるものなら[A-Za-z]と明示すると分かりやすい。長さが決まった短いパターンなので、不要な.*や複雑な繰り返しを入れる必要もない。
桁数の境界は特に間違えやすい。4桁の正常例だけではなく、3桁・5桁、空文字、区切り記号違いをテストへ加えると、仕様変更の影響を確認できる。IDの先頭ゼロには意味があるため、整数へ変換してから検査したり保存したりしない。文字列のまま保持して必要な場所でのみ数値部分を解釈する。
仕様を変更するときの確認点
例えば連番を5桁へ増やす変更では、{4}を{5}へ直すだけでなく、既存の4桁IDを引き続き認めるかを決める。新旧両形式を認める移行期間と、新形式だけを認める期間では期待値が違う。検証関数の変更が過去データを読めなくする場合もあるので、入力用の規則と保存済みデータの読み込み規則を安易に共有しない。単純な式でも、どの時点の仕様なのかが分かるテストがあると保守しやすい。
実行環境と関連情報
掲載コードはLinux上のCPython 3.12.14で実行した。OS固有のファイル操作や対話環境の違いは、本文に記した条件に従って扱う。
関連:測定ログを列に分ける:名前付きグループで正規表現を読む / pytestのparametrizeで境界値と入力パターンをまとめて検証する
- Python公式ドキュメント(2026年10月2日参照)
- Python公式ドキュメント(2026年10月2日参照)
