Python 逆引き集 | 文字列操作:特定文字列で始まるか判定する

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

Pythonで特定の文字列で始まるかどうかを判定する方法

ファイル名が「report_」で始まるか確認したい、URLが「https://」で始まるか検証したい、ログの行が「ERROR」で始まるか判定したい――プログラミングの現場では、「この文字列は特定の文字列で始まっているか?」を調べたい場面が非常に多くあります。この記事では、Pythonが標準で用意しているstartswith()メソッドを中心に、基本的な使い方から複数条件への対応、大文字・小文字を区別しない判定、そして実務でそのまま使えるテンプレートまで、プログラミング初心者の方でも確実に使いこなせるように順を追って解説します。

はじめに:文字列の先頭を調べる代表的な場面

文字列の「先頭」を判定したい場面は、実務のさまざまな場面で登場します。主な例を挙げます。

  • ファイル名の分類:「report_」で始まるファイルをレポートとして扱う
  • URLの検証:「https://」で始まるURLのみ許可する(セキュリティ上重要)
  • ログの解析:「ERROR」で始まる行だけを抽出する
  • コマンドの判定:「-」で始まる引数をオプションとして処理する
  • データの整形:特定のプレフィックスを持つデータだけ変換する

これらの判定は、文字列を1文字ずつ調べるのではなく、Pythonの標準メソッドを使えば1行で書けます。まずは基本構文から見ていきましょう。

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

startswith()の基本構文と挙動

startswith()は、文字列が指定した文字列で始まっていればTrue、そうでなければFalseを返すメソッドです。

# startswith()の基本例
filename = "report_2026.txt"

print(filename.startswith("report_"))  # True(report_で始まる)
print(filename.startswith("data_"))    # False(data_では始まらない)
print(filename.startswith("report_2026"))  # True(長い文字列でもOK)

# 大文字・小文字は区別される(重要な落とし穴)
print(filename.startswith("Report_"))  # False(大文字のRは一致しない)
print(filename.startswith(""))         # True(空文字列は「すべての文字列で始まる」と判定)
Python

ポイントを整理します。

  • 判定したい文字列を引数に渡すだけで、True/Falseが返る
  • 大文字・小文字は厳密に区別される(「Report_」と「report_」は別物)
  • 空文字列""を渡すと常にTrueになる(後述の注意点)

in演算子との違いに注意

「文字列に特定の文字列が含まれるか」を判定するin演算子は、先頭に限らずどこにでもマッチします。startswith()は「先頭から始まるか」の判定のみ行うため、目的に応じて使い分ける必要があります。

# startswith()とin演算子の違い
text = "sample_report.txt"

# startswith():先頭から始まるかを判定
print(text.startswith("report"))  # False(先頭は"sample"のため)

# in演算子:どこかに含まれるかを判定
print("report" in text)           # True(中盤に"report"が含まれる)
Python

「先頭だけ」を厳密に判定したい場合にinを使うと、文字列の途中にマッチしてもTrueになってしまい、意図しない判定結果になります。「〜で始まる」ならstartswith()、「〜を含む」ならinと、目的に合わせて選択してください。

否定(〜で始まらない)の判定

「〜で始まらない」場合の処理を書きたい場合は、not演算子を使います。

# 「〜で始まらない」の判定
filename = "image_001.jpg"

if not filename.startswith("report_"):
    print("レポートファイルではありません")  # この例では実行される
Python

発展編:複数条件と大文字・小文字への対応

複数の文字列のいずれかで始まるかを判定する

「.jpg」または「.png」で始まるか、のように複数の条件のいずれかを満たすかを判定したい場面もよくあります。従来はorで繋ぐ方法が使われていましたが、startswith()はタプル(複数の値をまとめたもの)を渡すことで、より簡潔に書けます。

# 方法1:orで繋ぐ(従来の方法。条件が増えると冗長になる)
filename = "photo.png"
print(filename.startswith(".jpg") or filename.startswith(".png"))  # True

# 方法2:タプルを渡す(推奨。条件をカンマ区切りで並べるだけ)
print(filename.startswith((".jpg", ".png")))  # True(どちらかで始まればTrue)

# 否定と組み合わせる例(画像ファイル以外を弾く)
if not filename.startswith((".jpg", ".png", ".gif")):
    print("対応していないファイル形式です")
Python

タプルを使う方法は、条件が増えてもorを繰り返さずに済み、コードの見通しがよくなります。複数条件の判定ではタプルを使うのがPythonの慣習と覚えておいてください。

大文字・小文字を区別せずに判定する

「Report_」でも「REPORT_」でも「report_」でも同じ扱いにしたい場合、判定前に文字列を小文字(または大文字)に揃えます。lower()やcasefold()を使うのが定番です。

# 大文字・小文字を区別せずに判定する例
filename = "REPORT_2026.txt"

# 小文字に揃えてから判定
print(filename.lower().startswith("report_"))  # True

# 判定する文字列側を大文字にしても同じ結果
print(filename.startswith(("REPORT_", "Report_", "report_")))  # True(タプル版)
Python

タプルで全パターンを列挙する方法も有効ですが、パターンが増えるほど冗長になります。一般には、比較対象の文字列を正規化(小文字化)してから判定する方がシンプルで保守性が高いです。厳密な照合が必要な場合は、前回の記事で解説したcasefold()を使うと、特殊な文字(ドイツ語のßなど)にも対応できます。

判定位置を指定する(start/end引数)

startswith()には、検索を開始する位置を指定するstart引数、終了位置を指定するend引数もあります。ただし、実務ではあまり頻繁には使われないため、覚えておく程度で構いません。

# start引数で検索開始位置を指定する例
text = "2026_report.txt"

# 5文字目(インデックス4)以降で判定
print(text.startswith("report", 4))  # True("2026_"の次から検索)
Python

実務編:具体的な活用例とテンプレート

例題1:安全なURLの検証

外部リンクを扱う際、「http://」または「https://」で始まるURLのみ許可する、という検証はセキュリティ上重要です。「javascript:」や「data:」などで始まるURLを弾くことで、XSS(クロスサイトスクリプティング)攻撃やフィッシングのリスクを低減できます。

# URLのスキームを検証するセキュアなテンプレート
def is_safe_url(url):
    # Noneや非文字列のガード
    if not isinstance(url, str):
        return False
    # 前後の空白を除去してから判定
    cleaned = url.strip()
    # https://またはhttp://で始まるもののみ許可(javascript:などを弾く)
    return cleaned.startswith(("https://", "http://"))

# 使用例
tests = ["https://example.com", "http://example.com", "javascript:alert(1)", "ftp://example.com"]
for u in tests:
    print(f"{u:>25} → {is_safe_url(u)}")
# https://example.com     → True
# http://example.com      → True
# javascript:alert(1)     → False(危険なスキームを弾く)
# ftp://example.com       → False
Python

例題2:ログファイルのフィルタリング

ログファイルから特定のレベルのエラーのみを抽出する例です。タプルを使った複数条件判定が活躍します。

# エラーレベルのログだけを抽出する例
logs = [
    "INFO: 処理を開始しました",
    "ERROR: ファイルが見つかりません",
    "WARNING: メモリ使用率が高いです",
    "ERROR: データベース接続に失敗",
    "DEBUG: 変数の値を確認"
]

# 「ERROR」または「CRITICAL」で始まる行だけを抽出
error_logs = [line for line in logs if line.startswith(("ERROR", "CRITICAL"))]

for line in error_logs:
    print(line)
# ERROR: ファイルが見つかりません
# ERROR: データベース接続に失敗
Python

リスト内包表記([line for line in logs if ...])は、リストから条件に合う要素だけを効率的に抽出するPythonの強力な構文です。先頭の判定と組み合わせることで、データのフィルタリング処理が簡潔に書けます。

例題3:コマンドライン引数の判定

ハイフン(-や--)で始まる引数をオプションとして処理する例です。これはCLIツールを作る際の基本パターンです。

# コマンドライン引数を判定する例
def process_args(args):
    for arg in args:
        if arg.startswith("--"):
            print(f"長いオプション: {arg}")      # --version など
        elif arg.startswith("-"):
            print(f"短いオプション: {arg}")       # -v など
        else:
            print(f"ファイル名または値: {arg}")    # input.txt など

# 使用例
process_args(["--verbose", "-o", "output.txt", "input.txt"])
# 長いオプション: --verbose
# 短いオプション: -o
# ファイル名または値: output.txt
# ファイル名または値: input.txt
Python

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

Noneや非文字列型への対策

外部データや辞書の値など、Noneや整数が混入する可能性がある場合、そのままstartswith()を呼ぶとエラーになります。型チェックのガードを入れるのが安全です。

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

print(safe_startswith("hello", "he"))  # True
print(safe_startstartswith(None, "he"))  # False(エラーにならない)
print(safe_startswith(123, "he"))       # False(数値型も安全に処理)
Python

ユーザー入力や外部APIのレスポンスを扱う場合は、必ず型チェックを行ってから判定する習慣を付けてください。こうした防御的プログラミングが、予期しないクラッシュやセキュリティインシデントを防ぎます。

空文字列を判定対象に含めるべきか

先述の通り、startswith("")は常にTrueになります。これは「空文字列は任意の文字列の先頭部分とみなせる」という論理によるものですが、実務では意図しない判定を招くことがあります。

# 空文字列は常にTrueになる例
print("hello".startswith(""))  # True(空文字列はどの文字列でも「先頭に含まれる」)
print("".startswith(""))       # True
Python

もし「空文字列は弾きたい」という要件がある場合は、あらかじめ空文字列チェックを行ってください。

# 空文字列を弾く例
def strict_startswith(text, prefix):
    if not isinstance(text, str) or not text:
        return False  # 空文字列はFalse
    return text.startswith(prefix)

print(strict_startswith("hello", "he"))  # True
print(strict_startswith("", "he"))        # False(空文字列を弾く)
Python

startswith()は「完全一致」ではない

startswith()は「先頭部分が一致するか」の判定であり、「文字列全体が一致するか(完全一致)」の判定ではありません。完全一致が必要な場合は==を使い分けてください。

# startswith()と完全一致(==)の違い
text = "report.txt"

print(text.startswith("report"))  # True(先頭が"report"で始まる)
print(text == "report")           # False(全体が"report"とは一致しない)
Python

セキュリティ上の考慮:プレフィックス判定は「入口の検査」

情報セキュリティの観点では、startswith()による検証は多層防御の一環です。特にURLやファイルパスの検証では、単なる文字列マッチングだけでなく、正規化(URLデコードやパスの解決)を行った上での検証が必要な場合があります。

# 単純なstartswith()では防げない攻撃の例(概念的な説明)
url1 = "https://example.com/../../../etc/passwd"  # パストラバーサルの試行
url2 = "https://example.com/%2e%2e/"              # URLエンコードされた攻撃

# startswith("https://")はTrueを返すが、危険なURLである可能性がある
print(url1.startswith("https://"))  # True(だが中身は危険)
Python

つまり、startswith()は「スキームの確認」として有効ですが、URLやパスの正当性の完全な保証にはなりません。後続の処理で、URLの構造解析、許可ドメインリストとの照合、パスの正規化など、より厳密な検証を行うよう設計してください。

まとめ

特定の文字列で始まるかどうかを判定するには、startswith()メソッドを使います。基本構文は文字列.startswith(判定文字列)で、タプルを渡すことで複数条件の「いずれか」も簡潔に表現できます。大文字・小文字を区別しない判定が必要な場合は、lower()やcasefold()で正規化してから判定するのが定番です。

注意すべきは、in演算子との使い分け(先頭限定かどうか)、Noneや非文字列型へのガード、空文字列が常にTrueになる挙動、そしてセキュリティ上の検証は多層防御の一環に過ぎない点です。これらを押さえ、紹介したテンプレートを実際に動かして挙動を確かめながら、堅牢で安全な文字列判定の実装に役立ててください。

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