Pythonのpathlibで拡張子を調べる:.tar.gzと隠しファイルで起きる意外な違い

Python

この記事について
この記事は、生成AIを活用した自動生成フローで作成しています。Python公式ドキュメントのpathlibの仕様をもとに、架空のファイル名を使って拡張子の扱いを整理します。

検証ステータス:📘 公式仕様確認済み・掲載コードの実機実行は未実施

ファイル名から拡張子を取り出す処理は簡単に見えます。しかし、report.csvなら期待どおりでも、backup.tar.gzや.envを混ぜると、思っていた結果と違うことがあります。原因は「最後の拡張子」「複数の拡張子」「先頭が点の名前」を、同じ規則で扱えると思い込むことです。今回はファイルを一つも開かず、名前だけを安全に比較します。

どんなときに役立つ?

メール添付の整理、バックアップの一覧、CSVを取り込む前のファイル種別確認などで、名前から処理先を決めたい場面があります。例えば、拡張子が.gzだからといって、必ず.tar.gz形式のバックアップとは限りません。逆に.envのように点から始まる設定ファイルを、拡張子envだと決めつけると分類を誤ります。

ここで重要なのは、拡張子はファイルの中身を検査した結果ではないという点です。名称の規則を読む処理と、ファイル形式の妥当性を調べる処理は別物です。自動処理でこの二つを混同すると、名前を変更しただけのファイルを正しい形式として受け入れてしまいます。

対象環境と準備

項目内容
対象Python 3.10以降を想定
使用するもの標準ライブラリpathlib
外部ライブラリ不要
入力架空のファイル名のみ
ファイル操作読み書き・削除なし
注意pathlibの一部の接尾辞仕様はPythonのバージョンによって異なる

Pythonが使える端末で、新しい作業フォルダにcheck_suffix.pyというファイルを作り、次のコードを保存します。実際の会社のファイル名や顧客名を入力する必要はありません。コードはファイルを読み込まず、文字列をPurePathとして解釈するだけです。

まずは五つの名前を比べる

from pathlib import PurePath

names = [
    "report.csv",
    "backup.tar.gz",
    ".env",
    "report.",
    "no_extension",
]

for name in names:
    p = PurePath(name)
    print(repr(name), "suffix=", repr(p.suffix),
          "suffixes=", p.suffixes, "stem=", repr(p.stem))

保存したら、ターミナルでpython check_suffix.pyを実行します。環境によってはpython3が必要です。出力の文字列を丸暗記するより、suffixとsuffixesが何を見ているかを確認しましょう。特にbackup.tar.gzでは、最後の接尾辞.gzと、連続する接尾辞の一覧['.tar', '.gz']が別々に表現されます。これは「圧縮ファイルであること」と「tarアーカイブであること」を一つの値から推測しないための手がかりです。

.envはUnix系でよく見る隠しファイル風の名前です。名前の先頭に点があるからといって、suffixが.envになるわけではありません。また、report.の末尾の点の扱いはPython 3.14で仕様が変わりました。バージョンを固定しないまま、このケースの出力を全環境共通だと説明するのは不正確です。自分の環境のpython --versionを確認し、公式ドキュメントの該当バージョンと比べてください。

図で見る:どの情報を使って分類する?

次の図は、ファイル名を受け取ったときに何を判断するかを示した概念図です。ファイルの内容を開いて検査する処理は含んでいません。

flowchart TD
    A["ファイル名を受け取る"] --> B{"知りたい情報は?"}
    B -->|"最後の接尾辞"| C["suffixを確認"]
    B -->|"複数の接尾辞"| D["suffixesを確認"]
    B -->|"ファイル形式の正しさ"| E["別途、中身の検証が必要"]
    C --> F["名前による仮分類"]
    D --> F

図の左側は名前から文字列を取り出すだけです。右側の「中身の検証」は別の処理になります。例えばbackup.tar.gzという名前でも、実体がgzipとして展開できるとは限りません。名前だけで検査済みの印を付けないことが、この図の最も重要な読み方です。

コードの動きを一行ずつ理解する

PurePath(name)は、実在するファイルを要求しません。文字列をパスとして扱うオブジェクトを作ります。したがって、存在しない架空名でも拡張子のルールを確認できます。実ファイルに触れないので、初心者が本番データを誤って更新する心配もありません。

p.suffixは最後の接尾辞を表します。p.suffixesは接尾辞を複数取得するためのプロパティです。p.stemは最後の接尾辞を除いた名前を得るときに使います。つまりbackup.tar.gzに対するstemは、一般にbackup.tarという形です。ここでbackupまで取りたい場合、単純にstemを一回呼ぶだけでは足りません。

repr()を使って表示しているのは、空文字列や末尾の点を見落とさないためです。画面上に何も表示されない場合、それが空文字なのか、空白文字なのかは通常のprint()では区別しにくくなります。診断用の小さなコードでは、見やすさより値の正確な確認を優先することがあります。

一条件を変える:tar.gzだけを特別扱いする

次はsuffixだけで判断する方法から、名前の末尾全体を確認する方法に切り替えます。変える条件は「複合拡張子を区別するかどうか」だけです。

from pathlib import PurePath

for name in ["backup.tar.gz", "photo.gz", "report.csv"]:
    p = PurePath(name)
    basic = p.suffix.lower()
    if p.name.lower().endswith(".tar.gz"):
        kind = "tar+gzip候補"
    elif basic == ".gz":
        kind = "gzip候補"
    else:
        kind = "その他"
    print(name, kind)

この比較ではbackup.tar.gzとphoto.gzを別の候補に分類できます。ただしendswith()も名前の文字列を比べているだけであり、ファイルの形式を保証しません。また、大文字小文字をそろえるlower()は、名称の表記ゆれを吸収するための処理です。OSのファイルシステムが大文字小文字を区別するかどうかとは別の問題です。

実務で使う場合、まず名称による候補の振り分けを行い、その後に形式に応じたライブラリで開けるかどうかを検証する二段階にすると、責任の境界が明確になります。自動展開や削除はこのサンプルでは行いません。

うまくいかないときの切り分け

症状原因の候補確認方法
suffixが空接尾辞なし、先頭の点などrepr(p.name)を見る
.tar.gzを一つで取得できないsuffixは最後の接尾辞を見るsuffixesや末尾一致を使う
末尾の点で結果が違うPythonのバージョン差バージョン別公式仕様を確認
ファイルが開けない名前と実体が一致しない可能性内容検査を別途実施
WindowsとLinuxで違うパス区切りや名前規則の違い対象OSとパス表現を確認

拡張子の取得だけを試したいなら、実際のファイル一覧を読み込む必要はありません。まず架空の名前で再現し、その後、業務データのコピーを使って条件を追加する方が安全です。問題が発生したときも、名前の解釈とファイルの読込失敗を別々に調べられます。

PurePathはパスの文字列を操作する用途に向いています。一方、存在確認や実際のファイル操作が必要ならPathを使う場面があります。ただし、この記事の目的は名前の判定です。存在確認を足してもファイル形式の保証にはならないため、不要なI/Oを増やさない方が分かりやすくなります。

業務での使い分けと安全な設計

ファイル整理では、suffixを使った簡単な分類が十分なことがあります。例えば、CSV候補を一覧に表示して人が選ぶだけなら、名前に基づく判定であることを明示すれば実用的です。反対に、受信ファイルを自動で展開し、別システムへ登録するような処理では、名前の確認だけで安全と判断しないでください。

バックアップの復旧手順では特に注意が必要です。.tar.gzという名称でも、破損している、別形式である、展開先に予期しないパスが含まれる、といった問題は残ります。展開処理には専用の検証と安全な展開先の設計が必要です。この記事のコードをそのまま自動展開の許可判定に使わないことが重要です。

また、拡張子のないファイルをすべて不正とみなす設計も避けましょう。Linuxでは実行ファイルや設定ファイルが拡張子を持たないこともあります。自分の業務で許可するファイルの種類を決め、そのルールに合わないものを確認待ちへ回す方が、単純な一律拒否より運用しやすい場合があります。

最後に、同じ.gzでも単一ファイルの圧縮とアーカイブを含む圧縮では処理方法が異なります。接尾辞の取得結果は次の処理を選ぶためのヒントにとどめ、検証の成功と混同しないようにします。ここを区別できると、拡張子を読む短いコードが、誤った自動処理を防ぐ設計上のチェックポイントになります。

公式情報・参考資料

検証範囲

Python標準ライブラリの公式仕様に基づく説明です。掲載したコードはこの制作実行ではPython実機で動かしていません。特に末尾の点の解釈はバージョン差があるため、利用するPythonの公式ドキュメントを確認してください。

文書情報

記事タイトル
Pythonのpathlibで拡張子を調べる:.tar.gzと隠しファイルで起きる意外な違い
作成日
更新日
Source URL
https://papanda925.com/?p=18547

ライセンス: 本記事のうち、当サイトが権利を有する本文・自作図表は、特記なき限り CC BY 4.0 で利用できます。生成AIを活用して作成・編集した内容を含みます。コードについて、別途ライセンス表示またはリンク先GitHubリポジトリのライセンスがある場合は、その条件を優先します。引用・第三者資料・画像・商標等は本ライセンスの対象外です。 利用ポリシー

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