Pythonで文字列が数字かどうかを判定する方法
フォームで入力された値が「本当に数値かどうか」を確認したい、CSVの列が数字で構成されているか検証したい、といった場面は、実務プログラミングで非常に多くあります。しかしPythonでは、数字の判定方法が複数存在し、それぞれ判定の範囲が微妙に異なります。
使い方を誤ると「見た目は数字なのにFalseになる」「数字ではないのにTrueになる」という混乱を招きます。
この記事では、isdigit()、isnumeric()、isdecimal()という3つのメソッドの違いと、実務で最も安全な判定手法まで、プログラミング初心者の方でも確実に理解できるように順を追って解説します。
はじめに:なぜ「数字かどうか」の判定が複雑なのか
「文字列が数字かどうか」という問いは、一見シンプルに思えますが、実際には「何を数字と呼ぶか」によって答えが変わります。たとえば"123"は誰もが数字と認めるでしょうが、"-5"(負の数)はどうでしょうか。"3.14"(小数)はどうでしょうか。
さらには全角の"123"や、上付き文字の"²"(2の2乗)は数字とみなすべきか、考える余地があります。Pythonはこの「幅広い数字の定義」をメソッドごとに分けて提供しており、使い分けの知識が求められるのです。
# 「数字」の定義によって判定結果が変わる例を先に見ておく
samples = ["123", "-5", "3.14", "123", "²"]
for s in samples:
print(f"{s!r:>10} digit={s.isdigit()} numeric={s.isnumeric()} decimal={s.isdecimal()}")
# '123' → 3つともTrue
# '-5' → 3つともFalse(符号はどのメソッドも許容しない)
# '3.14' → 3つともFalse(小数点は数字ではない)
# '123' → 3つともTrue(全角数字は認める)
# '²' → digit=True, numeric=True, decimal=False(細かい差異が出る)
Pythonまずこの「結果がメソッドによって違う」という事実を頭に入れておいてください。
以下、それぞれのメソッドを詳しく見ていきます。
基本編:isdigit()・isnumeric()・isdecimal()の使い方
isdigit()の基本構文と挙動
isdigit()は、文字列が数字(0〜9相当)だけで構成されているかを判定する最もポピュラーなメソッドです。
文字列が空の場合はFalseを返し、1文字でも数字以外が含まれていればFalseを返します。
# isdigit()の基本例
print("123".isdigit()) # True
print("abc".isdigit()) # False(数字ではない文字が含まれる)
print("12a".isdigit()) # False(途中に文字が混ざっている)
print("".isdigit()) # False(空文字は常にFalse)
print(" 123".isdigit()) # False(前後の空白も「数字ではない」扱い)
Python重要なポイントは2つあります。
第一に、前後の空白は許容されないこと。ユーザー入力などには空白が付随することが多いため、判定前にstrip()で除去するのが定番です。
第二に、マイナス記号(-)や小数点(.)は「数字ではない」と判定されること。"-5"や"3.14"は数値として自然ですが、isdigit()の世界ではFalseになります。
これは初学者が最もハマりやすい落とし穴です。
isnumeric()とisdecimal()との違い
3つのメソッドの違いは「どれだけ広い範囲の文字を数字と認めるか」にあります。
ざっくりと、広い順にisnumeric() > isdigit() > isdecimal()の関係にあります。
# 3つのメソッドの判定範囲の違い
print("123".isdecimal()) # True(半角数字は最も厳密なdecimalでもTrue)
print("123".isdecimal()) # True(全角数字もdecimalでTrue)
print("²".isdecimal()) # False(上付き数字はdecimalではない)
print("²".isdigit()) # True(digitは上付き数字を認める)
print("一".isnumeric()) # True(漢数字はnumericのみTrue)
print("Ⅷ".isnumeric()) # True(ローマ数字もnumericのみTrue)
Python実務の指針としては、「半角・全角のアラビア数字だけか」を厳密にチェックしたい場合はisdecimal()、日本語の入力を含む汎用的な数字判定ならisnumeric()、一般的な用途ではisdigit()、という使い分けが安全です。
ただし、どれも「符号や小数点を含む数値表記」を許容しないという制約は共通です。
実務テンプレート:入力値の検証
ユーザーからの年齢入力など、整数値のみを許容したい場面の定番テンプレートです。
空白の除去と、空文字チェックを忘れずに組み込んでいます。
# 整数入力を検証する実務テンプレート
user_input = " 25 " # 実際はinput()で取得する
cleaned = user_input.strip() # ① 前後の空白を除去
if cleaned.isdigit(): # ② 数字だけで構成されているか判定
age = int(cleaned) # ③ 安全に整数へ変換できる
print(f"年齢は{age}歳です")
else:
print("整数で入力してください")
Python発展編:符号や小数点を許容したい場合
isdigit()では判定できない「数値らしい文字列」
"-10"や"3.5"、"+100"のように、実務上「数字として妥当」と思える表記は、isdigit()系のメソッドでは判定できません。
こうしたケースでは、例外処理(try-except)を使って変換を試みる手法が最も確実で推奨されます。
# try-exceptで「実際に数値へ変換できるか」を判定する方法
def is_number(text):
"""符号や小数点を含む数値表記もOKとする判定関数"""
try:
float(text) # float()で変換を試みる(失敗するとValueErrorが発生)
return True
except ValueError:
return False # 変換できなければ数値ではない
# 使用例
print(is_number("123")) # True
print(is_number("-5")) # True(符号を許容)
print(is_number("3.14")) # True(小数を許容)
print(is_number("1e5")) # True(指数表記も許容)
print(is_number("abc")) # False
print(is_number("")) # False(空文字は変換不可)
print(is_number(" 12 ")) # True(float()は前後の空白を許容する)
Pythonこの方法の強みは、Python自身の数値変換ルールと完全に一致する点です。isdigit()がTrueでもfloat()で変換に失敗する例外的な文字(例:一部のUnicode数字)は存在しますが、逆に「変換できるのにisdigit()がFalse」という実務上の問題は解消されます。
また、float()は前後の空白を自動で許容するため、strip()の手間も省けます(厳密に空白を弾きたい場合は別途判定が必要です)。
整数のみを許容する厳密なバージョン
「小数は許容したくないが、負の数は許したい」という要件もよくあります。
その場合は、変換後に整数であるかを追加で確認します。
# 整数のみ(符号は許容)を判定する関数
def is_integer(text):
try:
value = float(text)
except ValueError:
return False
# 整数値に変換しても値が変わらなければ整数とみなす
return value.is_integer()
print(is_integer("42")) # True
print(is_integer("-7")) # True(負の整数もOK)
print(is_integer("3.0")) # True(値としては整数)
print(is_integer("3.14")) # False(小数はNG)
print(is_integer("abc")) # False
Python注意すべき重要なポイント
isdigit()がTrueでもint()でエラーになることがある
これはセキュリティや堅牢性の観点で非常に重要な注意点です。isdigit()がTrueを返す文字列の中には、実は標準のint()変換に失敗するものが存在します。
代表例が上付き数字の"²"や、一部の全角文字です。
# isdigit()はTrueなのにint()でエラーになる例
text = "²" # 上付きの2
print(text.isdigit()) # True
# int(text) # ValueError: invalid literal for int()(エラーになる!)
Pythonつまり「isdigit()でチェックしてからint()にかければ安全」という発想には穴があります。
最終的に数値として使う目的なら、try-exceptによる変換の成否こそが真実です。
変換できなければ数値ではない、というシンプルで確実な原則を持ちましょう。
「数字だけ」でなく「数字を含むか」の判定は別物
「文字列の中に数字が1つでも含まれるか」を調べたい場合は、isdigit()は使えません。
任意の1文字が数字であることを調べる方法を使います。
# 文字列の中に数字が含まれるかを判定する
text = "abc123def"
# any()とisdigit()の組み合わせ(1文字でも数字があればTrue)
has_digit = any(ch.isdigit() for ch in text)
print(has_digit) # True
# 「数字だけで構成されているか」の判定(今回の本題)
print(text.isdigit()) # False(判定の意図がまったく違う)
Pythonisdigit()は「すべての文字が数字であるか」の判定であり、「含むか」の判定とは論理が逆です。
この違いを混同すると、まったく意図しない条件分岐になります。
Noneや非文字列型への対策
外部データや辞書の値など、Noneや整数が混入する可能性がある場合、そのままisdigit()を呼ぶとエラーになります。型チェックのガードを入れるのが安全です。
# 安全に判定するためのガード付きテンプレート
def safe_isdigit(value):
# 文字列型でなければFalse(Noneや数値を誤って通さない)
if not isinstance(value, str):
return False
return value.strip().isdigit()
print(safe_isdigit("123")) # True
print(safe_isdigit(None)) # False(エラーにならない)
print(safe_isdigit(123)) # False(整数型は対象外)
print(safe_isdigit(" 12 ")) # True(空白は許容する設計)
Pythonセキュリティ面での考慮:判定と変換はセットで考える
情報セキュリティの視点では、「入力が数字か判定する」の目的はその後の処理を安全にするためです。
SQLの注入対策、コマンド実行時の引数検証、整数オーバーフロー対策など、数値として扱う前提の処理を汚染データから守ることが本質です。
そのため、判定ロジックと実際の変換・利用処理が乖離している設計は避け、「変換に成功したものだけを次の処理へ渡す」という構造にしてください。
前述のtry-exceptパターンは、この思想に最も忠実な実装と言えます。
# セキュアな入力処理の流れ(検証→変換→利用を一連の流れで)
def process_quantity(raw_input):
try:
quantity = int(raw_input.strip()) # 検証と変換を同時に行う
except ValueError:
return None # 不正な入力はここで遮断
if quantity < 0 or quantity > 10000: # 業務上の妥当な範囲もチェック
return None
return quantity # ここを通った値のみが安全に利用できる
Python単に「数字かどうか」を見るのではなく、「期待する型に変換でき、かつ業務ルールの範囲内か」まで確認するのが、実務レベルの検証です。
まとめ
文字列が数字かどうかを判定するには、主にisdigit()、isnumeric()、isdecimal()の3つのメソッドを使います。判定の幅はisnumeric()が最も広く、isdecimal()が最も厳密ですが、いずれも符号(-)や小数点(.)を含む数値表記は認めないという共通の制約があります。
空白も許容されないため、判定前のstrip()は欠かせません。
そして最も重要なのは、数値として利用する目的なら、try-exceptによる変換の成否を判定基準にすることです。isdigit()がTrueでもint()で失敗する文字が存在するため、変換を試みて成功したものだけを次の処理に渡す設計が、最も堅牢でセキュアな実装です。
まずは紹介したテンプレートを実際に動かし、それぞれのメソッドがどのような文字列に対してどう応えるかを確かめてみてください。
