【Python】Python 〜 ファイル形式判定を考える 〜

◾️はじめに

https://dk521123.hatenablog.com/entry/2025/12/19/233012

で合成データを考えた際に、
そのファイルの元になったファイル形式
(当面では、CSV/DSV(Delimiter Separated Values), Parquet)
が分からないと、実装難しいかなっと思った。
そこで、今回は、Pythonでファイル形式判定を考える。

目次

【0】ファイル形式判定ライブラリ
【1】python-magic
 1)使用上の注意
 2)インストール
 3)サンプル
【2】filetype
 1)使用上の注意
 2)インストール
 3)サンプル
【3】mimetypes
 1)使用上の注意
 2)サンプル
【4】ファイル形式判定を自作で考える
 例1:CSV/DSV, Parquetの判定

【0】ファイル形式判定ライブラリ

ライブラリ名 精度・対応数 導入のしやすさ 判定方法
python-magic ★★★ ★★☆ マジックナンバー
filetype ★★☆ ★★★ マジックナンバー
mimetypes ★☆☆ ★★★ (標準) 拡張子

【1】python-magic

* Unix系のfileコマンドで使用されているlibmagicというライブラリのラッパー

https://pypi.org/project/python-magic/

1)使用上の注意

* Linuxコマンドのラッパーのため、OS依存する
 => Windowsなどの環境では、別途libmagicのインストールが必要になる場合がある

2)インストール

MACの場合

brew install libmagic

pip install python-magic

3)サンプル

import magic

# ファイルパスから判定
file_type = magic.from_file("sample.csv")
print(file_type)  # CSV ASCII text

# MIMEタイプ(image/jpegなど)で取得したい場合
mime = magic.Magic(mime=True)
print(mime.from_file("sample.csv"))  # text/csv

print("-----")

# ファイルパスから判定
file_type = magic.from_file("sample.dsv")
print(file_type)  # ASCII text, with CRLF line terminators

# MIMEタイプ(image/jpegなど)で取得したい場合
mime = magic.Magic(mime=True)
print(mime.from_file("sample.dsv"))  # text/plain

参考文献
https://omomuki-tech.com/archives/2390

【2】filetype

* 外部ライブラリに依存せず、軽量・純Python製
* 動作が速く、インストールが簡単

1)使用上の注意

* サポートしているファイル形式が
 主要なもの(画像、動画、音声、アーカイブなど)に限定
 => CSV, JSONなどのテキストは判定できなかった

2)インストール

pip install filetype

3)サンプル

import filetype

kind = filetype.guess('sample.pdf')

if kind is None:
    print('判定できませんでした')
else:
    print('拡張子:', kind.extension) # 拡張子: pdf
    print('MIMEタイプ:', kind.mime) # MIMEタイプ: application/pdf

【1】mimetypes

* Python標準ライブラリ

1)使用上の注意

* 主に拡張子から判定するため、ファイルの中身が壊れていたり、
 拡張子が偽装されている場合は正しく判定できない

2)サンプル

import mimetypes

mime_type, _ = mimetypes.guess_type('sample.pdf')
print(mime_type)  # 'application/pdf'

mime_type, _ = mimetypes.guess_type('sample.csv')
print(mime_type)  # 'text/csv'

mime_type, _ = mimetypes.guess_type('sample.dsv')
print(mime_type)  # 'None'

mime_type, _ = mimetypes.guess_type('my_metadata.json')
print(mime_type)  # 'application/json'

mime_type, _ = mimetypes.guess_type('compose.yml')
print(mime_type)  # 'None'

mime_type, _ = mimetypes.guess_type('demo2.py')
print(mime_type)  # 'text/x-python'

mime_type, _ = mimetypes.guess_type('sample.parquet')
print(mime_type)  # None

【4】ファイル形式判定を自作で考える

例1:CSV/DSV, Parquetの判定

判定のロジック

* Parquetはバイナリデータ、CSVはテキストデータという大きな違いがあるため、
 以下のフローで判定。

 + Parquetの判定: 先頭4バイトが PAR1 かどうかを確認する。
 + CSV/テキストの判定: PAR1 でなければテキストとして読み込み、
  csv.Sniffer で区切り文字を推測

サンプル

import csv

def identify_file_format(file_path):
    with open(file_path, 'rb') as f:
        # 1. Parquetの判定 (先頭4バイトを確認)
        header = f.read(4)
        if header == b'PAR1':
            return {"format": "Parquet", "delimiter": None}

    # 2. CSV/DSVの判定
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            sample = f.read(2048) # 先頭2KBをサンプルとして読み込む
            
            # csv.Snifferで区切り文字を推測
            # delimiters引数に判定したい文字をリストアップできます
            sniffer = csv.Sniffer()
            dialect = sniffer.sniff(sample, delimiters=[',', '|', '\t', ';'])
            
            return {
                "format": "CSV/DSV",
                "delimiter": dialect.delimiter
            }
    except Exception as e:
        return {"format": "Unknown or Error", "details": str(e)}

# テスト実行
result = identify_file_format("sample.csv")
# 形式: CSV/DSV, 区切り文字: ,
print(f"形式: {result['format']}, 区切り文字: {result['delimiter']}")

result = identify_file_format("sample.dsv")
# 形式: CSV/DSV, 区切り文字: |
print(f"形式: {result['format']}, 区切り文字: {result['delimiter']}")

result = identify_file_format("sample.parquet")
# 形式: Parquet, 区切り文字: None
print(f"形式: {result['format']}, 区切り文字: {result['delimiter']}")

関連記事

AWS上に合成データ生成システムを構築する 〜 構想編 〜
https://dk521123.hatenablog.com/entry/2025/12/19/233012
Python 〜 Faker 〜
https://dk521123.hatenablog.com/entry/2025/12/20/001758
Python SDV 〜 入門編 〜
https://dk521123.hatenablog.com/entry/2025/12/21/000330
Pandas ~ Parquet ~
https://dk521123.hatenablog.com/entry/2024/09/06/004125