Python 逆引き集 | 文字列操作:文字列が英数字か判定する

Python Python
スポンサーリンク
スポンサーリンク

Pythonで文字列が英数字かどうかを判定する方法

パスワードに「英数字のみ」を要求したい、商品コードが英字と数字の組み合わせだけで構成されているか確認したい、ユーザー入力から記号を排除したい――プログラミングの現場では、「この文字列は英字と数字だけでできているか?」を判定したい場面が非常に多くあります。この記事では、isalnum()というメソッドを中心に、正しい使い方、意外な落とし穴、そして「英数字のみ」を厳密に判定する実務テンプレートまで、プログラミング初心者の方でも確実に使いこなせるように順を追って解説します。

はじめに:英数字判定で知っておくべき基本

「英数字」とは、アルファベット(a〜z、A〜Z)と数字(0〜9)のことです。たとえば"abc123"や"A7B9"は英数字のみの文字列ですが、"abc-123"(ハイフンあり)や"abc 123"(空白あり)は英数字のみとは言えません。

Pythonでこの判定を行う代表的な方法はisalnum()メソッドですが、実はこのメソッドには重要な特性があります。日本語(ひらがな・漢字)に対してもTrueを返す点です。これはバグではなく、Unicode(世界中の文字体系を扱う国際標準)の設計によるもので、実務でこの特性を知らずに使うと「日本語入力を通してしまう」という不具合につながります。

この記事では以下の流れで解説します。

  • 基本編:isalnum()の基本構文と挙動、日本語にTrueになるという落とし穴
  • 実務編:「アルファベット+数字(0〜9)のみ」を厳密に判定する方法
  • 注意点:アンダースコアの扱い、None対策、セキュリティ上の考慮

まずは基本から確実に押さえていきましょう。

基本編:isalnum()メソッドの使い方

isalnum()の基本構文と挙動

isalnum()は、文字列が「文字(alpha)または数字(numeric)」だけで構成されているかを判定するメソッドです。アルファベットのみ、数字のみ、英数字の混合、いずれもTrueになります。一方、記号や空白が1文字でも含まれていればFalseを返し、空文字列に対してもFalseを返します。

# isalnum()の基本例
print("abc123".isalnum())    # True(英字と数字の混合)
print("abcXYZ".isalnum())    # True(英字のみでもOK)
print("12345".isalnum())     # True(数字のみでもOK)
print("abc-123".isalnum())   # False(ハイフンが含まれる)
print("abc 123".isalnum())   # False(空白が含まれる)
print("abc_123".isalnum())   # False(アンダースコアも含まれる!後述)
print("".isalnum())          # False(空文字は常にFalse)
Python

ポイントを整理します。

  • 英字のみ・数字のみ・混合はすべてTrue
  • 記号(-、!、@など)はFalse
  • 空白(半角スペース、タブ、全角スペースなど)はFalse
  • アンダースコア(_)はFalse(後で詳しく解説します)

重要な落とし穴:日本語にもTrueを返す

ここが本記事で最も重要なポイントのひとつです。isalnum()は「英数字(a〜z、0〜9)だけかどうか」を判定しているのではなく、「Unicodeで『文字』または『数字』と分類されるものだけかどうか」を判定しています。そのため、日本語にもTrueを返します。

# 日本語に対してもisalnum()はTrueを返す(重要な落とし穴)
print("あいうえお".isalnum())  # True(ひらがな)
print("漢字123".isalnum())     # True(漢字+数字)
print("あいうえお".isalpha())  # True(isalpha()も同様に日本語を通す)

# アクセント付き文字や全角英数字もTrue
print("café123".isalnum())    # True(éも「文字」として認識)
print("ABC123".isalnum())  # True(全角英数字)
Python

つまり、「英数字(半角アルファベット+半角数字)のみを許可したい」という実務上の要件に対して、isalnum()をそのまま使うと日本語入力を通してしまい、想定外のデータがシステムに流入する原因になります。この違いを明確に理解した上で、次章の厳密な判定方法を覚えておいてください。

isalnum()が適切なケース

一方で、isalnum()は「記号や空白を含まない、文字と数字だけの入力かどうか」を広く確認したい場合には有効です。たとえば、住所の入力から記号を排除したい場合や、ファイル名の安全性をざっくり確認したい場合などです。

# 「記号や空白を含まないか」を確認する例
filename = "report2026"
if filename.isalnum():
    print(f"「{filename}」は記号を含まない安全な名前です")
# report2026 → True

filename2 = "report 2026!"
print(filename2.isalnum())  # False(空白と!を検出できる)
Python

「英数字に厳密に限定しないが、記号や空白は禁止したい」という要件なら、isalnum()は簡潔で効果的な選択肢です。

実務編:「英数字(a〜z、A〜Z、0〜9)のみ」を厳密に判定する

方法1:set(集合)との比較で厳密に判定する

「アルファベット52文字+数字10文字のみで構成されているか」を厳密に判定する、最も直感的で保守性の高い方法は、文字列を集合に変換し、許容する文字の集合に含まれるかを調べる方法です。

# 英数字(大文字小文字の英字+数字)のみで構成されているか厳密に判定
def is_alnum_strict(text):
    if not isinstance(text, str) or not text:
        return False  # 文字列型でない、または空文字はFalse
    # 許容する文字の集合:小文字26 + 大文字26 + 数字10
    allowed = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
    # 文字列を集合にし、許容集合にすべて含まれているかを確認
    return set(text) <= allowed

# 使用例
print(is_alnum_strict("abc123"))    # True
print(is_alnum_strict("A7B9xY"))    # True(混合でもOK)
print(is_alnum_strict("abc-123"))   # False(ハイフンを弾く)
print(is_alnum_strict("abc 123"))   # False(空白を弾く)
print(is_alnum_strict("あいうえお"))   # False(日本語を弾く!)
print(is_alnum_strict("ABC123"))  # False(全角も弾く!)
print(is_alnum_strict(None))        # False(エラーにならない)
Python

この方法の最大の利点は、「何を許容するか」をコード上で一目瞭然に明示できることです。ハイフンやアンダースコアを許可したい場合は、allowedに1文字追加するだけで柔軟に対応できます。

# ハイフンとアンダースコアも許容するバージョン
def is_alnum_with_symbols(text):
    if not isinstance(text, str) or not text:
        return False
    allowed = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_")
    return set(text) <= allowed

print(is_alnum_with_symbols("user-001"))    # True(ハイフンを許容)
print(is_alnum_with_symbols("user_name99")) # True(アンダースコアを許容)
print(is_alnum_with_symbols("user.name"))   # False(ピリオドは弾く)
Python

方法2:正規表現(reモジュール)で判定する

正規表現を使う方法も一般的です。^[a-zA-Z0-9]+$というパターンは、「先頭から末尾まで(^と$)、a〜z、A〜Z、0〜9のいずれかの文字が1文字以上(+)続く」という意味です。

# 正規表現で英数字のみを判定する例
import re

def is_alnum_re(text):
    if not isinstance(text, str):
        return False
    # 全文が英数字のみかを判定(^は先頭、$は末尾、+は1文字以上)
    return re.fullmatch(r"[a-zA-Z0-9]+", text) is not None

print(is_alnum_re("abc123"))     # True
print(is_alnum_re("abc-123"))    # False
print(is_alnum_re("あいうえお"))   # False(日本語も正しく弾ける)
print(is_alnum_re("ABC123"))  # False(全角も弾ける)
print(is_alnum_re(""))           # False(+は1文字以上を要求するため)
Python

正規表現は強力で短く書けますが、パターンを読み解く知識が必要です。チームでの保守を想定する場合は、集合を使う方法1の方が「何を許可しているか」が直感的に伝わり、初心者にも優しいという利点があります。

方法3:isalnum()とisascii()を組み合わせる

「英数字っぽい文字かつ、ASCII(基本文字セット)の範囲内か」という2条件を組み合わせる方法もあります。isascii()は文字列がASCIIの範囲(コード0〜127)内にあるかを判定し、日本語や全角文字を弾くことができます。

# isalnum() + isascii()の組み合わせ
def is_alnum_ascii(text):
    if not isinstance(text, str) or not text:
        return False
    # ① 英数字っぽい文字(記号や空白ではない)かつ、② ASCII範囲(日本語や全角を弾く)
    return text.isalnum() and text.isascii()

print(is_alnum_ascii("abc123"))    # True
print(is_alnum_ascii("あいうえお"))   # False(isascii()で弾ける)
print(is_alnum_ascii("ABC123"))  # False(全角はASCII外のため弾ける)
print(is_alnum_ascii("abc-123"))   # False(isalnum()で弾ける)
Python

この方法はコードが短く、意図も伝わりやすいです。ただし「ASCII範囲=英数字」ではないため(記号や空白もASCII範囲です)、必ずisalnum()とのAND条件にする点は外せません。

実務テンプレート:パスワードの英数字チェック

「英数字を含む」ことの確認と、「英数字のみで構成する」ことの確認は別物です。ここでは両方のパターンを紹介します。

英数字のみで構成されるかを検証する

# 実務テンプレート:英数字のみ・8文字以上のパスコード検証
def validate_passcode(code):
    # ① 型と空文字のガード
    if not isinstance(code, str):
        return False, "パスコードは文字列である必要があります"
    code = code.strip()  # ② 前後の空白を除去
    if not code:
        return False, "パスコードが空です"

    # ③ 英数字のみかを厳密に判定
    allowed = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
    if not set(code) <= allowed:
        return False, "パスコードは英数字(a-z, A-Z, 0-9)のみ使用できます"

    # ④ 業務ルール(長さ)のチェック
    if len(code) < 8:
        return False, "パスコードは8文字以上にしてください"

    return True, "OK"

# 使用例
tests = ["abcd1234", "abcd-1234", "あいうえお", "abc123"]
for t in tests:
    ok, msg = validate_passcode(t)
    print(f"{t!r:>12} → {ok} ({msg})")
# 'abcd1234'   → True (OK)
# 'abcd-1234'  → False (英数字のみで入力してください)
# 'あいうえお'  → False (英数字のみで入力してください)
# 'abc123'     → False (8文字以上にしてください)
Python

「英字と数字の両方を含むか」を検証する

より強固なパスワードポリシーとして、「英字と数字の両方を少なくとも1文字ずつ含む」という要件もよくあります。これはisalnum()では判定できないため、別のアプローチが必要です。

# 「英字と数字の両方を含むか」を検証する例
def has_letter_and_digit(text):
    if not isinstance(text, str) or not text:
        return False
    has_letter = any(c.isalpha() and c.isascii() for c in text)
    has_digit = any(c.isdigit() and c.isascii() for c in text)
    return has_letter and has_digit

print(has_letter_and_digit("abc123"))    # True(両方含む)
print(has_letter_and_digit("abcdef"))    # False(数字がない)
print(has_letter_and_digit("123456"))    # False(英字がない)
print(has_letter_and_digit("abcあいう"))   # False(日本語は英字としてカウントしない)
Python

なお、現代のパスワードポリシーでは「英字+数字+記号」の複合や、最低文字数の確保が推奨されます。英数字チェックは要件の一部として位置づけ、セキュリティ設計全体の中で考慮してください。

注意すべき重要なポイント

アンダースコア(_)は含まれない

プログラミングでは変数名としてuser_nameのようなアンダースコア付きの文字列をよく目にするため、「アンダースコアも英数字の一部では?」と思うかもしれません。しかしisalnum()ではアンダースコアはFalseになります。

# アンダースコアはisalnum()ではFalse
print("user_name".isalnum())  # False(_は「文字」でも「数字」でもない)
print("user-name".isalnum())  # False(ハイフンも同様)
Python

もし「英数字+アンダースコア」を許可したい場合は、前章の集合を使う方法で許容文字に_を追加するか、正規表現を^[a-zA-Z0-9_]+$と変更してください。

「英数字を含むか」と「英数字のみか」の混同に注意

「文字列の中に英数字が1つでも含まれるか」を調べたい場合は、isalnum()は使えません。任意の1文字が英数字であることを調べる方法を使います。

# 文字列の中に英数字が含まれるかを判定する
text = "ユーザーIDはabc123です"

# any()とisalnum()の組み合わせ(1文字でも英数字があればTrue)
has_alnum = any(ch.isalnum() for ch in text)
print(has_alnum)  # True

# 「英数字だけで構成されているか」の判定(今回の本題)
print(text.isalnum())  # False(日本語が含まれるため。判定の意図がまったく違う)
Python

isalnum()は「すべての文字が英数字(またはUnicode文字/数字)であるか」の判定であり、「含むか」の判定とは論理が逆です。この違いを混同すると、まったく意図しない条件分岐になります。

Noneや非文字列型への対策

外部データや辞書の値など、Noneや整数が混入する可能性がある場合、そのままisalnum()を呼ぶとエラーになります。型チェックのガードを入れるのが安全です(前述のテンプレートでも対応済みです)。

# 安全に判定するためのガード付き例
def safe_isalnum(value):
    # 文字列型でなければFalse(Noneや数値を誤って通さない)
    if not isinstance(value, str):
        return False
    return value.strip().isalnum()

print(safe_isalnum("abc123"))  # True
print(safe_isalnum(None))      # False(エラーにならない)
print(safe_isalnum(12345))     # False(整数型は対象外)
print(safe_isalnum(" abc "))   # True(空白は除去して許容する設計)
Python

セキュリティ上の考慮:英数字判定は「入口の検査」

情報セキュリティの観点では、入力値の文字種検証は多層防御の最初の関門です。英数字以外を弾くことで、SQLインジェクションやコマンドインジェクションで悪用される記号類('、;、|など)の混入を未然に防ぐ効果があります。ただし、文字種検証だけで injection 対策が完結することはありません。パラメータ化クエリやエスケープ処理など、後続の防御策は必ず併用してください。

# 英数字検証を入口に、後続の防御も怠らない構造の例
def create_product(product_code):
    ok, msg = validate_passcode(product_code)  # 入口での検証(多層防御の第1層)
    if not ok:
        raise ValueError(msg)
    # 第2層以降:SQLは必ずパラメータ化クエリで実行する
    # query = "INSERT INTO products (code) VALUES (%s)"
    # cursor.execute(query, (product_code,))  # ← 文字列連結でSQLを作らない!
Python

「検証に通ったから安全」ではなく「検証を通った後も、安全な実装で処理する」という多層防御の発想が、セキュリティスペシャリストとしての正しい姿勢です。

まとめ

文字列が英数字かどうかを判定する場合、isalnum()は基本的な選択肢ですが、日本語や全角文字にもTrueを返すという特性を必ず理解しておく必要があります。「半角英数字(a〜z、A〜Z、0〜9)のみ」を厳密に求める実務では、許容文字の集合との比較、正規表現、isalnum()+isascii()の組み合わせなど、より制御の効く手法を選択してください。そして、アンダースコアが含まれない点、「含むか」と「のみか」の違い、None対策のガードを意識することで、堅牢な入力検証が実現できます。英数字判定はセキュリティの入口として機能しますが、単独では完璧な防御になり得ない点も踏まえ、多層防御の一環として位置づけましょう。まずは紹介したテンプレートを動かし、日本語や全角文字に対してそれぞれの方法がどう応答するかを確かめてみてください。

タイトルとURLをコピーしました