Python glob完全ガイド|ファイル検索の基本から応用まで徹底解説

Pythonのglobモジュールを使うと、ワイルドカード(*、?、[])を用いてファイルやディレクトリを効率的に検索できます。この記事では、基本的な使い方から、再帰的な検索、正規表現との組み合わせ、ファイル名のみの取得方法まで、実践的なコード例とともに解説。大量のファイルを一括処理したい、特定パターンのファイルを抽出したいといった悩みを解決できます。

目次

Pythonのglobモジュールとは

python+file+search

Pythonのglobモジュールは、ファイルシステム上のファイルやディレクトリを効率的に検索するための標準ライブラリです。Unixシェルで使われるパターンマッチング機能を提供し、ワイルドカードを使った柔軟なファイル検索が可能になります。

このモジュールは、特定の条件に合致する複数のファイルを一括で取得したい場合に非常に便利です。例えば、「拡張子が.txtのファイルをすべて取得したい」「特定の命名規則に従ったファイルだけを処理したい」といったニーズに対して、簡潔なコードで対応できます。

globモジュールの主な特徴は以下の通りです:

  • Pythonの標準ライブラリに含まれているため、追加のインストールが不要
  • ワイルドカード(*、?、[]など)を使った直感的なパターン指定が可能
  • 再帰的なディレクトリ検索にも対応
  • os.listdir()やos.walk()よりもシンプルな記述でファイル検索が実現できる
  • メモリ効率の良いイテレータ処理もサポート

globという名前は「global」に由来しており、Unixのコマンドラインで使われる「グロブパターン」と同じ概念を実装しています。プログラム内でファイルパスを動的に取得する必要がある場合、このモジュールを活用することで、コードの可読性を保ちながら柔軟なファイル操作が実現できます。

データ分析や機械学習のプロジェクトでは、複数のデータファイルを読み込む処理が頻繁に発生します。また、ログファイルの解析、画像ファイルの一括処理、バックアップファイルの管理など、実務のさまざまな場面でglobモジュールは活躍します。Pythonでファイル操作を行う際には、必ず押さえておきたい基本的なツールの一つと言えるでしょう。

“`html

globモジュールの基本的な使い方

python+file+search

Pythonのglobモジュールを使いこなすためには、まず基本的な使用方法をしっかりと理解することが重要です。このセクションでは、実際にコードを動かしながら学べるよう、インポート方法からファイル検索の基本まで段階的に解説していきます。初めてglobモジュールを使う方でも、このセクションの内容を実践すればすぐに活用できるようになります。

インポート方法と基本構文

globモジュールを使用するには、まずPythonスクリプトの冒頭でインポートする必要があります。globはPythonの標準ライブラリに含まれているため、追加のインストール作業は一切不要です。以下のコードでインポートを行います。

import glob

globモジュールの最も基本的な関数はglob.glob()です。この関数は指定したパターンに一致するファイルやディレクトリのパスをリストとして返します。基本的な構文は次の通りです。

結果 = glob.glob(パターン)

実際の使用例を見てみましょう。以下のコードは、カレントディレクトリ内のすべてのファイルとディレクトリを取得します。

import glob

# カレントディレクトリ内のすべてのファイル・ディレクトリを取得
files = glob.glob('*')
print(files)

このコードを実行すると、カレントディレクトリにあるすべてのファイル名やディレクトリ名がリスト形式で出力されます。返り値はPythonのリスト型なので、for文での反復処理やリスト操作が簡単に行える点が大きな利点です。

テスト用ファイルの準備方法

globモジュールの動作を確認するには、テスト用のファイル構造を準備しておくと理解が深まります。ここでは、Pythonのコードでテスト用ファイルを自動生成する方法を紹介します。

以下のコードを実行すると、様々なパターンのテストファイルが作成されます。

import os

# テスト用ディレクトリの作成
os.makedirs('test_dir', exist_ok=True)

# 各種テストファイルの作成
test_files = [
    'test_dir/file1.txt',
    'test_dir/file2.txt',
    'test_dir/data1.csv',
    'test_dir/data2.csv',
    'test_dir/image1.png',
    'test_dir/image2.jpg',
    'test_dir/document.pdf'
]

for file_path in test_files:
    with open(file_path, 'w') as f:
        f.write('テストデータ')

print('テストファイルの作成が完了しました')

このコードでは、test_dirという名前のディレクトリを作成し、その中にテキストファイル、CSVファイル、画像ファイルなど、様々な拡張子のファイルを生成しています。exist_ok=Trueを指定することで、既にディレクトリが存在していてもエラーが発生しないようになっています。

さらに複雑な階層構造のテスト環境を作成したい場合は、以下のようなコードを使用できます。

import os

# 階層構造を持つテストディレクトリの作成
directories = [
    'test_dir/sub1',
    'test_dir/sub2',
    'test_dir/sub1/subsub1'
]

for directory in directories:
    os.makedirs(directory, exist_ok=True)

# 階層ごとにファイルを配置
nested_files = [
    'test_dir/root.txt',
    'test_dir/sub1/file_a.txt',
    'test_dir/sub2/file_b.txt',
    'test_dir/sub1/subsub1/deep_file.txt'
]

for file_path in nested_files:
    with open(file_path, 'w') as f:
        f.write('テストデータ')

print('階層構造を持つテスト環境の作成が完了しました')

このようにテスト環境を整えることで、globモジュールの様々なパターンマッチング機能を実際に試しながら学習できます。

単一ファイルの検索方法

globモジュールを使って特定のファイルを検索する基本的な方法を見ていきましょう。最もシンプルなケースは、ファイル名が完全にわかっている場合です。

例えば、test_dirディレクトリ内のfile1.txtというファイルを検索する場合は、次のように記述します。

import glob

# 特定のファイルを検索
result = glob.glob('test_dir/file1.txt')
print(result)

# 実行結果例: ['test_dir/file1.txt']

この例では、ワイルドカードを使わずに完全なファイルパスを指定しています。ファイルが存在すれば、そのパスを含む1要素のリストが返されます。ファイルが存在しない場合は空のリスト[]が返される点に注意が必要です。

検索結果が存在するかどうかを確認する実用的なコード例は以下の通りです。

import glob

# ファイルの存在確認
file_path = 'test_dir/file1.txt'
result = glob.glob(file_path)

if result:
    print(f'ファイルが見つかりました: {result[0]}')
else:
    print('ファイルが見つかりませんでした')

また、カレントディレクトリを基準とした相対パスだけでなく、絶対パスでの検索も可能です。

import glob
import os

# 絶対パスでの検索
absolute_path = os.path.abspath('test_dir/file1.txt')
result = glob.glob(absolute_path)
print(result)

単一ファイルの検索は、os.path.exists()関数でも実現できますが、globを使うことで後述するワイルドカードパターンへの拡張が容易になります。globモジュールはファイル検索の一貫したインターフェースを提供するため、単一ファイル検索から複雑なパターンマッチングまで、同じ関数で対応できるのが大きな利点です。

検索結果はリスト型で返されるため、複数のファイルパスを一度に処理する場合でも、コードの構造を変更する必要がありません。

import glob

# 複数ファイルの検索結果を処理
files = glob.glob('test_dir/*.txt')

for file in files:
    print(f'処理中: {file}')
    # ここでファイルの読み込みや加工処理を行う

このように、globモジュールの基本的な使い方をマスターすることで、Pythonでのファイル操作がより直感的で効率的になります。

“`

“`html

ワイルドカード(特殊文字)を使ったパターンマッチング

python+file+search

Python globモジュールの最大の特徴は、ワイルドカードと呼ばれる特殊文字を使って柔軟なパターンマッチングができることです。これにより、複雑な条件でファイルを検索する際も、シンプルな記述で目的のファイルを抽出できます。ここでは、globで使用できる主要なワイルドカードとその使い方について詳しく解説します。

アスタリスク(*)で任意の文字列を指定する

アスタリスク(*)は、0文字以上の任意の文字列にマッチする最も基本的なワイルドカードです。ファイル名の一部分だけが分かっている場合や、特定のパターンを持つファイルをまとめて取得したい場合に非常に便利です。

import glob

# .txtで終わるすべてのファイルを取得
txt_files = glob.glob('*.txt')
print(txt_files)
# 出力例: ['file1.txt', 'file2.txt', 'document.txt']

# dataで始まるすべてのファイルを取得
data_files = glob.glob('data*')
print(data_files)
# 出力例: ['data.csv', 'data_backup.xlsx', 'data123.json']

# testを含むすべてのファイルを取得
test_files = glob.glob('*test*')
print(test_files)
# 出力例: ['test.py', 'my_test_file.txt', 'pytest.ini']

アスタリスクは複数回使用することも可能で、より複雑なパターンマッチングを実現できます。例えば、「report_*_2024.pdf」のようなパターンで、特定の年度のレポートファイルだけを抽出することができます。

クエスチョンマーク(?)で任意の1文字を指定する

クエスチョンマーク(?)は、厳密に1文字だけにマッチするワイルドカードです。ファイル名の文字数が決まっている場合や、特定の位置の文字だけが異なるファイルを検索する際に活用します。

import glob

# file1.txt, file2.txt, fileA.txtなどにマッチ
single_char_files = glob.glob('file?.txt')
print(single_char_files)
# 出力例: ['file1.txt', 'file2.txt', 'fileA.txt']

# data_2024_01.csv, data_2024_12.csvなどにマッチ
month_files = glob.glob('data_2024_??.csv')
print(month_files)
# 出力例: ['data_2024_01.csv', 'data_2024_12.csv']

# test1.py, testA.pyにマッチするが、test12.pyにはマッチしない
test_files = glob.glob('test?.py')
print(test_files)
# 出力例: ['test1.py', 'testA.py']

クエスチョンマークは、アスタリスクと組み合わせることで、より精密なパターン指定が可能になります。例えば、「log_????.txt」とすれば、ログファイル名が4文字のものだけを抽出できます。

角括弧([])で特定の文字を指定する

角括弧([])を使用すると、指定した複数の文字のいずれか1文字にマッチさせることができます。特定の文字パターンに限定してファイルを検索したい場合に非常に有効です。

import glob

# file1.txt, file2.txt, file3.txtのみにマッチ
numbered_files = glob.glob('file[123].txt')
print(numbered_files)
# 出力例: ['file1.txt', 'file2.txt', 'file3.txt']

# dataA.csv, dataB.csv, dataC.csvにマッチ
letter_files = glob.glob('data[ABC].csv')
print(letter_files)
# 出力例: ['dataA.csv', 'dataB.csv', 'dataC.csv']

# report_a.pdf, report_e.pdf, report_i.pdfなど母音のみ
vowel_files = glob.glob('report_[aeiou].pdf')
print(vowel_files)
# 出力例: ['report_a.pdf', 'report_e.pdf', 'report_i.pdf']

角括弧内には、カンマで区切ることなく文字を連続して記述します。この方法により、特定の文字セットに限定した柔軟な検索が実現できます。

ハイフン(-)で文字の範囲を指定する

角括弧の中でハイフン(-)を使用すると、文字コードの範囲で連続した文字群を指定できます。数字や英字の範囲を指定する際に、すべての文字を列挙する必要がなくなり、コードが簡潔になります。

import glob

# file0.txt~file9.txtまでの数字ファイルすべてにマッチ
digit_files = glob.glob('file[0-9].txt')
print(digit_files)
# 出力例: ['file0.txt', 'file1.txt', ..., 'file9.txt']

# chapterA.pdf~chapterZ.pdfまでの大文字アルファベットにマッチ
chapter_files = glob.glob('chapter[A-Z].pdf')
print(chapter_files)
# 出力例: ['chapterA.pdf', 'chapterB.pdf', ..., 'chapterZ.pdf']

# data01.csv~data99.csvなど2桁の数字にマッチ
two_digit_files = glob.glob('data[0-9][0-9].csv')
print(two_digit_files)
# 出力例: ['data01.csv', 'data23.csv', 'data99.csv']

# 小文字と数字の組み合わせ
mixed_files = glob.glob('log_[a-z0-9].txt')
print(mixed_files)
# 出力例: ['log_a.txt', 'log_5.txt', 'log_z.txt']

複数の範囲を組み合わせることも可能で、「[a-zA-Z]」とすれば大文字小文字両方のアルファベットにマッチします。この機能により、コードの可読性を保ちながら広範囲な検索条件を設定できます。

否定パターン([!])で特定文字を除外する

角括弧内で先頭に感嘆符(!)を付けると、指定した文字以外にマッチする否定パターンを作成できます。特定のファイルを除外したい場合に便利な機能です。

import glob

# file0.txt以外のfileX.txtにマッチ
non_zero_files = glob.glob('file[!0].txt')
print(non_zero_files)
# 出力例: ['file1.txt', 'file2.txt', 'fileA.txt'](file0.txtは除外)

# 数字以外の1文字を持つファイルにマッチ
non_digit_files = glob.glob('data[!0-9].csv')
print(non_digit_files)
# 出力例: ['dataA.csv', 'dataB.csv', 'data_.csv']

# 母音以外の子音を持つファイルにマッチ
consonant_files = glob.glob('char_[!aeiou].txt')
print(consonant_files)
# 出力例: ['char_b.txt', 'char_c.txt', 'char_d.txt']

# 複数文字の除外も可能
exclude_files = glob.glob('log[!123].txt')
print(exclude_files)
# 出力例: ['log4.txt', 'log5.txt', 'logA.txt'](log1.txt, log2.txt, log3.txtは除外)

否定パターンは、特定のバックアップファイルやテンポラリファイルを除外してメインのファイルだけを処理したい場合などに活躍します。ただし、除外したい文字が多い場合は、逆に含めたい文字を指定する方が簡潔になることもあります。

特殊文字のエスケープ処理

ファイル名に実際のワイルドカード文字(*, ?, [, ]など)が含まれている場合、これらの文字を文字通りに検索するためのエスケープ処理が必要になります。Python globでは、角括弧を使ったエスケープ方法が利用できます。

import glob

# ファイル名に実際に[が含まれる場合のエスケープ
# "file[1].txt"という名前のファイルを検索
escaped_bracket = glob.glob('file[[]1].txt')
print(escaped_bracket)
# 出力例: ['file[1].txt']

# アスタリスクを含むファイル名の検索
# "data*.txt"という名前のファイルを検索
escaped_asterisk = glob.glob('data[*].txt')
print(escaped_asterisk)
# 出力例: ['data*.txt']

# クエスチョンマークを含むファイル名の検索
# "test?.py"という名前のファイルを検索
escaped_question = glob.glob('test[?].py')
print(escaped_question)
# 出力例: ['test?.py']

また、バックスラッシュを含むWindowsのパスを扱う場合は、raw文字列(r”)を使用することで、エスケープ処理の煩雑さを軽減できます。

import glob

# Windows環境でのパス指定(raw文字列を使用)
windows_files = glob.glob(r'C:\Users\Documents\*.txt')
print(windows_files)

# 通常の文字列でバックスラッシュをエスケープする場合
windows_files_escaped = glob.glob('C:\\Users\\Documents\\*.txt')
print(windows_files_escaped)

特殊文字のエスケープは、実際のファイルシステムで特殊な命名規則を持つファイルを扱う際に重要になります。特に、自動生成されたファイル名や外部システムから取得したファイルを処理する場合は、予期しない文字が含まれる可能性があるため、適切なエスケープ処理を心がけましょう。

“`

“`html

glob関数の引数と詳細設定

python+file+search

Python globモジュールのglob関数には、基本的なパターンマッチング以外にも、さまざまな引数を指定することでより柔軟なファイル検索が可能になります。これらの引数を活用することで、再帰的な検索やルートディレクトリの変更、隠しファイルの取得など、より高度なファイル操作を実現できます。本セクションでは、glob関数の主要な引数について詳しく解説していきます。

recursive引数で再帰的にファイルを取得する

glob関数のrecursive引数をTrueに設定することで、サブディレクトリを含めた再帰的なファイル検索が可能になります。この機能を利用する際は、パターンに「**」(ダブルアスタリスク)を使用します。

import glob

# サブディレクトリを含むすべての.txtファイルを検索
files = glob.glob('**/*.txt', recursive=True)
print(files)

# ルートから全階層のPythonファイルを検索
python_files = glob.glob('**/*.py', recursive=True)
for file in python_files:
    print(file)

recursive引数を指定せずに「**」パターンを使用した場合、再帰的な検索は実行されませんので、必ずrecursive=Trueを明示的に指定する必要があります。この引数を活用することで、深い階層構造を持つプロジェクトでも効率的にファイルを収集できます。

root_dir引数でルートディレクトリを指定する

root_dir引数は、Python 3.10以降で導入された新しい機能で、検索の起点となるディレクトリを明示的に指定できます。この引数を使用すると、カレントディレクトリを変更することなく、任意のディレクトリを基準としたファイル検索が可能になります。

import glob

# /home/user/documentsディレクトリを起点に検索
files = glob.glob('*.txt', root_dir='/home/user/documents')
print(files)

# 相対パスで指定することも可能
files = glob.glob('**/*.py', root_dir='./project', recursive=True)
for file in files:
    print(file)

root_dir引数を使用することで、プログラム内で複数の異なるディレクトリに対して検索を行う際に、コードの可読性と保守性が向上します。また、返されるパスはroot_dirからの相対パスとなるため、パスの処理が簡潔になります。

dir_fd引数の活用方法

dir_fd引数は、ファイルディスクリプタを使用して検索の基準ディレクトリを指定する高度な機能です。この引数は主にUnix系システムで利用され、既に開いているディレクトリのファイルディスクリプタを指定することで、セキュアで効率的なファイル操作が可能になります。

import glob
import os

# ディレクトリをオープンしてファイルディスクリプタを取得
dirfd = os.open('/path/to/directory', os.O_RDONLY | os.O_DIRECTORY)

try:
    # ファイルディスクリプタを使用して検索
    files = glob.glob('*.txt', dir_fd=dirfd)
    print(files)
finally:
    # ファイルディスクリプタをクローズ
    os.close(dirfd)

dir_fd引数は、特に権限管理が厳格な環境や、同時に複数のディレクトリを操作する必要がある場合に有用です。この機能はすべてのオペレーティングシステムでサポートされているわけではないため、使用する際はプラットフォームの互換性を確認する必要があります。

include_hidden引数で隠しファイルを含める

Python 3.11以降で追加されたinclude_hidden引数を使用すると、ドット(.)で始まる隠しファイルやディレクトリも検索対象に含めることができます。デフォルトではglob関数は隠しファイルを除外しますが、この引数をTrueに設定することで明示的に含めることが可能です。

import glob

# 通常の検索(隠しファイルは除外される)
files = glob.glob('*')
print('通常:', files)

# 隠しファイルも含める
files_with_hidden = glob.glob('*', include_hidden=True)
print('隠しファイル含む:', files_with_hidden)

# 再帰的検索で隠しディレクトリ内も検索
all_files = glob.glob('**/*', recursive=True, include_hidden=True)
for file in all_files:
    print(file)

この引数は、システムファイルや設定ファイルを含めた完全なファイルリストを取得する必要がある場合に特に有用です。ただし、隠しファイルには重要なシステム設定が含まれることが多いため、これらのファイルを操作する際は十分な注意が必要です。また、include_hidden引数は比較的新しい機能であるため、古いバージョンのPythonでは使用できないことに留意してください。

“`

“`html

ファイルとディレクトリの分類取得

python+file+directory

globモジュールでパス一覧を取得した後、ファイルとディレクトリを区別して処理したいケースは頻繁に発生します。Python標準ライブラリのos.pathモジュールやpathlibモジュールと組み合わせることで、取得したパスを効率的に分類できます。このセクションでは、glob検索の結果からファイルのみ、ディレクトリのみを抽出する具体的な方法を解説します。

ファイル名のみを取得する方法

globで取得したパスの中からファイルだけを抽出するには、os.path.isfile()関数を使用します。この関数は指定されたパスがファイルであればTrue、ディレクトリやシンボリックリンクであればFalseを返すため、リスト内包表記と組み合わせることで簡潔にファイルだけをフィルタリングできます。

import glob
import os

# すべてのパスを取得
all_paths = glob.glob('data/*')

# ファイルのみを抽出
files_only = [path for path in all_paths if os.path.isfile(path)]

print("ファイル一覧:")
for file in files_only:
    print(file)

また、pathlibモジュールを使用するとよりモダンで読みやすいコードが書けます。pathlibのPathオブジェクトにはis_file()メソッドが備わっており、オブジェクト指向的なアプローチが可能です。

from pathlib import Path

# Pathオブジェクトでファイルを取得
files_only = [path for path in Path('data').glob('*') if path.is_file()]

print("ファイル一覧:")
for file in files_only:
    print(file)

さらに、ファイル名のみ(パスを除いた部分)を取得したい場合は、os.path.basename()関数やpathlibのname属性を使用します。

import glob
import os

files = [f for f in glob.glob('data/*') if os.path.isfile(f)]

# ファイル名のみを取得
file_names = [os.path.basename(f) for f in files]

print(file_names)
# 出力例: ['document.txt', 'image.png', 'script.py']

ディレクトリ名のみを取得する方法

ファイルとは逆に、ディレクトリのみを抽出する場合はos.path.isdir()関数を使用します。この関数はパスがディレクトリの場合にTrueを返すため、サブディレクトリの一覧を取得する際に非常に便利です。

import glob
import os

# すべてのパスを取得
all_paths = glob.glob('project/*')

# ディレクトリのみを抽出
directories_only = [path for path in all_paths if os.path.isdir(path)]

print("ディレクトリ一覧:")
for directory in directories_only:
    print(directory)

pathlibを使用した場合はis_dir()メソッドで同様の処理が実現できます。pathlibの利点は、パス操作がメソッドチェーンで直感的に記述できる点にあります。

from pathlib import Path

# ディレクトリのみを取得
directories = [path for path in Path('project').glob('*') if path.is_dir()]

# ディレクトリ名のみを表示
for directory in directories:
    print(directory.name)

実際の開発現場では、特定の構造を持つプロジェクト内のサブディレクトリを列挙し、各ディレクトリに対して一括処理を行うような場面でこの手法が活用されます。

import glob
import os

# サブディレクトリのみを取得
subdirs = [d for d in glob.glob('src/*') if os.path.isdir(d)]

# 各ディレクトリ内のPythonファイル数をカウント
for subdir in subdirs:
    py_files = glob.glob(f'{subdir}/*.py')
    print(f'{os.path.basename(subdir)}: {len(py_files)}個のPythonファイル')

階層構造を含むパスの取得方法

深い階層構造を持つディレクトリから、階層情報を保持したままファイルやディレクトリを取得する場合、recursive=Trueオプションと**パターンを組み合わせることが重要です。この方法により、すべてのサブディレクトリを再帰的に探索し、完全なパスを取得できます。

import glob
import os

# 再帰的にすべてのファイルを取得(階層構造を含む)
all_files = glob.glob('project/**/*', recursive=True)

# ファイルのみをフィルタリング
files_with_paths = [f for f in all_files if os.path.isfile(f)]

print("階層構造を含むファイルパス:")
for file_path in files_with_paths:
    print(file_path)
# 出力例:
# project/src/main.py
# project/src/utils/helper.py
# project/tests/test_main.py

階層の深さを可視化するために、パスの階層レベルを計算して表示することもできます。

import glob
import os

files = glob.glob('project/**/*.py', recursive=True)

for file in files:
    # パス区切り文字の数から階層の深さを計算
    depth = file.count(os.sep)
    indent = '  ' * (depth - 1)
    filename = os.path.basename(file)
    print(f'{indent}└─ {filename} ({file})')

pathlibを使用すると、階層構造の分析がさらに簡単になります。parts属性でパスの各要素にアクセスできるため、階層ごとの処理が直感的に記述できます。

from pathlib import Path

# 再帰的にすべてのPythonファイルを取得
python_files = list(Path('project').rglob('*.py'))

# 階層構造を保持したまま情報を表示
for file in python_files:
    # 相対パスとして表示
    relative_path = file.relative_to('project')
    depth = len(relative_path.parts) - 1
    print(f'{"  " * depth}└─ {file.name} (階層: {depth})')

また、特定の階層レベルのファイルだけを取得したい場合は、パスの区切り文字の数を条件にフィルタリングできます。

import glob
import os

# すべてのファイルを再帰的に取得
all_files = glob.glob('data/**/*', recursive=True)

# 2階層目のファイルのみを抽出(data/subdir/file.txt)
second_level_files = [
    f for f in all_files 
    if os.path.isfile(f) and f.count(os.sep) == 2
]

print("2階層目のファイル:")
for file in second_level_files:
    print(file)

階層構造を含むパスの取得は、大規模なプロジェクトでのファイル管理やログファイルの整理、バックアップ処理などで頻繁に使用される重要なテクニックです。ただし、再帰的な検索は処理時間がかかる可能性があるため、対象ディレクトリの規模に応じて適切な条件設定を行うことが推奨されます。

“`

正規表現との組み合わせによる高度な検索

python+regex+file

globモジュールのワイルドカード機能は便利ですが、より複雑な条件でファイルを抽出したい場合には限界があります。そこで、globで取得したファイルリストに対して正規表現を適用することで、高度なパターンマッチングが可能になります。Pythonの標準ライブラリであるreモジュールとglobを組み合わせることで、ファイル名の形式を厳密に検証したり、特定の命名規則に従ったファイルのみを抽出したりできます。

re.match()関数との連携方法

globで取得したファイルリストに対してre.match()関数を適用することで、より厳密なファイル名の検証が可能です。re.match()は文字列の先頭からパターンマッチングを行うため、ファイル名の形式を正確にチェックするのに適しています。

import glob
import re

# globで候補ファイルを取得
files = glob.glob('data/*.txt')

# 正規表現パターンを定義(例:data_数字4桁.txt)
pattern = re.compile(r'data_\d{4}\.txt$')

# パターンに一致するファイルのみを抽出
matched_files = [f for f in files if pattern.match(f.split('/')[-1])]

for file in matched_files:
    print(file)

このコードでは、まずglobで.txtファイルをすべて取得し、その後re.match()で「data_」の後に4桁の数字が続くファイルのみを抽出しています。globで大まかな絞り込みを行い、正規表現で詳細な条件を適用するという二段階のフィルタリングが効率的です。

繰り返し回数を指定した正規表現の活用

正規表現の量指定子を使用することで、特定の文字パターンの繰り返し回数を厳密に指定できます。これにより、ファイル名の長さや形式を細かく制御した検索が実現できます。

import glob
import re

# すべてのファイルを取得
all_files = glob.glob('logs/*')

# 様々な繰り返しパターンの例
patterns = {
    '3桁の数字': re.compile(r'log_\d{3}\.log$'),
    '2~4文字の英字': re.compile(r'[a-z]{2,4}_log\.txt$'),
    '最低5文字': re.compile(r'\w{5,}\.dat$'),
    '正確に8文字': re.compile(r'^[A-Za-z]{8}\.csv$')
}

for description, pattern in patterns.items():
    matched = [f for f in all_files if pattern.search(f.split('/')[-1])]
    print(f'{description}: {matched}')

量指定子には{n}(正確にn回)、{n,}(n回以上)、{n,m}(n回以上m回以下)といったバリエーションがあります。ファイル名の命名規則が厳格に定められているプロジェクトでは、この手法が非常に有効です。

グループ単位での正規表現適用

正規表現のグループ機能を使うと、ファイル名の一部を抽出したり、複数の部分パターンをまとめて処理したりできます。これは、ファイル名から情報を取り出す際に特に便利です。

import glob
import re

# ファイルを取得
files = glob.glob('reports/*.pdf')

# グループを使ったパターン(例:report_2024_01_15.pdf)
pattern = re.compile(r'report_(\d{4})_(\d{2})_(\d{2})\.pdf$')

for file in files:
    filename = file.split('/')[-1]
    match = pattern.match(filename)
    
    if match:
        year, month, day = match.groups()
        print(f'ファイル: {filename} - 日付: {year}年{month}月{day}日')

この例では、括弧()を使ってグループを定義し、match.groups()でそれぞれの値を取り出しています。また、名前付きグループ(?P<name>...)を使用すると、より可読性の高いコードが書けます。

import glob
import re

files = glob.glob('products/*.json')

# 名前付きグループの使用
pattern = re.compile(r'(?P<category>[a-z]+)_(?P<id>\d+)_(?P<version>v\d+)\.json$')

for file in files:
    filename = file.split('/')[-1]
    match = pattern.match(filename)
    
    if match:
        info = match.groupdict()
        print(f"カテゴリ: {info['category']}, ID: {info['id']}, バージョン: {info['version']}")

文字種別を限定する特殊シーケンス

正規表現の特殊シーケンスを活用すると、特定の文字種別に限定した検索が容易になります。これにより、数字のみ、英字のみ、英数字のみといった細かい条件を簡潔に記述できます。

import glob
import re
import os

files = glob.glob('data/*.*')

# 様々な特殊シーケンスの活用例
patterns = {
    '数字のみのファイル名': re.compile(r'^\d+\.\w+$'),
    '英字で始まるファイル': re.compile(r'^[a-zA-Z]'),
    '英数字とアンダースコアのみ': re.compile(r'^\w+\.\w+$'),
    '空白を含まないファイル': re.compile(r'^\S+$'),
    '英小文字と数字のみ': re.compile(r'^[a-z0-9]+\.')
}

for description, pattern in patterns.items():
    matched = [f for f in files if pattern.match(os.path.basename(f))]
    print(f'\n{description}:')
    for m in matched:
        print(f'  - {m}')

主な特殊シーケンスには以下のようなものがあります。

  • \d:数字(0-9)に一致
  • \D:数字以外に一致
  • \w:英数字とアンダースコアに一致
  • \W:英数字とアンダースコア以外に一致
  • \s:空白文字に一致
  • \S:空白文字以外に一致

これらを組み合わせることで、ファイル名の形式を非常に柔軟かつ厳密に検証できます。

複数条件を組み合わせた検索方法

実際の開発現場では、単一の条件ではなく複数の条件を組み合わせてファイルを検索する必要があることが多くあります。論理演算子や正規表現の選択パターンを使うことで、複雑な条件を効率的に処理できます。

import glob
import re
import os

# 複数のディレクトリからファイルを取得
all_files = glob.glob('**/*.*', recursive=True)

# OR条件:拡張子がcsvまたはxlsxまたはjson
pattern_or = re.compile(r'\.(csv|xlsx|json)$')

# AND条件:2024を含み、かつtestまたはprodを含む
def check_and_condition(filename):
    has_year = bool(re.search(r'2024', filename))
    has_env = bool(re.search(r'(test|prod)', filename))
    return has_year and has_env

# NOT条件:tempやbackupを含まない
pattern_not = re.compile(r'(temp|backup)', re.IGNORECASE)

# すべての条件を組み合わせた検索
filtered_files = [
    f for f in all_files
    if pattern_or.search(f)  # OR条件
    and check_and_condition(os.path.basename(f))  # AND条件
    and not pattern_not.search(f)  # NOT条件
]

for file in filtered_files:
    print(file)

さらに高度な例として、複数のパターンを動的に組み合わせる方法もあります。

import glob
import re

def advanced_file_search(base_pattern, conditions):
    """
    複数の正規表現条件を組み合わせてファイルを検索
    
    Args:
        base_pattern: globのパターン
        conditions: 辞書形式の条件('include'と'exclude'のリスト)
    """
    files = glob.glob(base_pattern, recursive=True)
    
    # include条件(いずれかに一致する必要がある)
    if conditions.get('include'):
        include_patterns = [re.compile(p) for p in conditions['include']]
        files = [f for f in files 
                if any(pattern.search(f) for pattern in include_patterns)]
    
    # exclude条件(すべてに一致しない必要がある)
    if conditions.get('exclude'):
        exclude_patterns = [re.compile(p) for p in conditions['exclude']]
        files = [f for f in files 
                if not any(pattern.search(f) for pattern in exclude_patterns)]
    
    return files

# 使用例
conditions = {
    'include': [r'\d{4}-\d{2}-\d{2}', r'report'],  # 日付形式またはreportを含む
    'exclude': [r'draft', r'\.tmp$', r'_old']  # draftや.tmpや_oldを含まない
}

result = advanced_file_search('documents/**/*.pdf', conditions)
print(f'検索結果: {len(result)}件')
for r in result:
    print(f'  {r}')

この手法を使うと、複雑なビジネスロジックに基づいたファイル検索を、保守性の高いコードで実装できます。条件を辞書形式で管理することで、設定ファイルから読み込んだり、ユーザー入力に基づいて動的に変更したりすることも容易になります。

“`html

glob関数の実践的な応用例

python+file+search

ここまで学んできたglob関数の基本機能を、実際の開発現場で使える実践的なテクニックとして具体的に紹介します。日常的なファイル操作タスクを効率化するための応用例を、実行可能なコード例とともに解説していきます。

大文字小文字を区別しない検索方法

Python globモジュールはデフォルトでファイルシステムの大文字小文字の扱いに従いますが、クロスプラットフォーム対応やユーザー入力に対応する際には大文字小文字を区別せずに検索したい場合があります。この課題を解決するためには、glob結果を取得した後にフィルタリングする方法が効果的です。

import glob
import os

def case_insensitive_glob(pattern):
    """大文字小文字を区別しないglob検索"""
    # すべてのファイルを取得
    all_files = glob.glob('*')
    
    # パターンを小文字に変換してマッチング
    pattern_lower = pattern.lower()
    matched_files = [f for f in all_files 
                     if f.lower().endswith(pattern_lower)]
    
    return matched_files

# 使用例:.TXT、.txt、.Txtなどすべてにマッチ
txt_files = case_insensitive_glob('.txt')
print(txt_files)

また、より汎用的なアプローチとして、pathlib モジュールと組み合わせることでOSの違いを吸収できます:

from pathlib import Path

def glob_case_insensitive(directory, extension):
    """pathlibを使った大文字小文字非依存の検索"""
    path = Path(directory)
    all_files = path.glob('*')
    
    return [f for f in all_files 
            if f.suffix.lower() == extension.lower()]

# 実行例
files = glob_case_insensitive('.', '.txt')

特定の拡張子ファイルの一括取得

特定の拡張子を持つファイルを一括で取得することは、データ処理やバッチ処理で頻繁に必要となる操作です。Python globを活用すれば、複数の拡張子に対応した柔軟なファイル取得が実現できます。

import glob

# 単一の拡張子の場合
csv_files = glob.glob('*.csv')
print(f"CSVファイル: {csv_files}")

# 複数の拡張子を同時に取得
def get_files_by_extensions(extensions):
    """複数の拡張子のファイルを取得"""
    all_files = []
    for ext in extensions:
        files = glob.glob(f'*.{ext}')
        all_files.extend(files)
    return all_files

# 使用例:テキスト関連ファイルをすべて取得
text_files = get_files_by_extensions(['txt', 'md', 'csv', 'log'])
print(f"テキストファイル: {text_files}")

サブディレクトリを含めた特定拡張子の検索も可能です:

# 再帰的に.pyファイルをすべて取得
python_files = glob.glob('**/*.py', recursive=True)
print(f"Pythonファイル数: {len(python_files)}")

# 特定フォルダ配下のExcelファイルを取得
excel_files = glob.glob('data/**/*.xlsx', recursive=True)

複数階層にわたるファイル検索

プロジェクトが大規模になると、複数階層のディレクトリ構造からファイルを検索する必要が生じます。recursive=True オプションと ** パターンを組み合わせることで、深い階層構造でも効率的に目的のファイルを見つけられます。

import glob
import os

# すべてのサブディレクトリからJSONファイルを取得
json_files = glob.glob('**/*.json', recursive=True)

# 階層ごとにファイルをグループ化
def group_files_by_depth(pattern):
    """階層の深さごとにファイルを分類"""
    files = glob.glob(pattern, recursive=True)
    grouped = {}
    
    for file in files:
        depth = file.count(os.sep)
        if depth not in grouped:
            grouped[depth] = []
        grouped[depth].append(file)
    
    return grouped

# 使用例
files_by_depth = group_files_by_depth('**/*.py')
for depth, files in sorted(files_by_depth.items()):
    print(f"階層{depth}: {len(files)}個のファイル")

特定の深さまでに限定した検索も実装できます:

def glob_with_max_depth(pattern, max_depth):
    """最大階層を指定したglob検索"""
    all_files = glob.glob(pattern, recursive=True)
    filtered = [f for f in all_files 
                if f.count(os.sep) = max_depth]
    return filtered

# 2階層までのPythonファイルを取得
shallow_files = glob_with_max_depth('**/*.py', 2)

適切な形式のファイルのみ読み込む方法

ファイル名のパターンだけでなく、ファイルの内容や属性も含めて適切な形式のファイルのみを選別することは、データ品質管理において重要です。glob検索結果に対して追加の検証を行うことで、処理対象を適切に絞り込めます。

import glob
import os

def get_valid_files(pattern, min_size=0, max_size=None):
    """サイズ条件を満たすファイルのみ取得"""
    files = glob.glob(pattern, recursive=True)
    valid_files = []
    
    for file in files:
        if not os.path.isfile(file):
            continue
        
        size = os.path.getsize(file)
        
        # サイズチェック
        if size  min_size:
            continue
        if max_size and size > max_size:
            continue
        
        valid_files.append(file)
    
    return valid_files

# 使用例:100KB以上10MB以下のCSVファイルを取得
csv_files = get_valid_files('data/**/*.csv', 
                           min_size=100*1024, 
                           max_size=10*1024*1024)
print(f"条件に合致: {len(csv_files)}個")

ファイルの内容を検証する例も紹介します:

def get_valid_text_files(pattern, encoding='utf-8'):
    """正しくエンコードされたテキストファイルのみ取得"""
    files = glob.glob(pattern, recursive=True)
    valid_files = []
    
    for file in files:
        try:
            with open(file, 'r', encoding=encoding) as f:
                # 最初の数行を読んで検証
                f.read(1024)
            valid_files.append(file)
        except (UnicodeDecodeError, PermissionError):
            # 読み込めないファイルはスキップ
            continue
    
    return valid_files

# UTF-8として正しく読めるテキストファイルのみ取得
valid_texts = get_valid_text_files('docs/**/*.txt')

画像ファイルの一括読み込み処理

画像処理や機械学習のプロジェクトでは、大量の画像ファイルを効率的に読み込む必要があります。Python globを使えば、複数の画像形式を一度に取得し、バッチ処理を実装できます。

import glob
import os

def get_image_files(directory):
    """一般的な画像形式のファイルをすべて取得"""
    image_extensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tiff', 'webp']
    image_files = []
    
    for ext in image_extensions:
        # 小文字と大文字の両方に対応
        pattern = os.path.join(directory, f'**/*.{ext}')
        image_files.extend(glob.glob(pattern, recursive=True))
        
        # 大文字バージョンも検索
        pattern_upper = os.path.join(directory, f'**/*.{ext.upper()}')
        image_files.extend(glob.glob(pattern_upper, recursive=True))
    
    return list(set(image_files))  # 重複削除

# 使用例
images = get_image_files('images')
print(f"画像ファイル数: {len(images)}")

実際の画像読み込み処理と組み合わせた例:

from PIL import Image
import glob

def load_images_batch(pattern, target_size=None):
    """画像ファイルを一括で読み込む"""
    image_paths = glob.glob(pattern, recursive=True)
    loaded_images = []
    
    for path in image_paths:
        try:
            img = Image.open(path)
            
            # リサイズが指定されている場合
            if target_size:
                img = img.resize(target_size)
            
            loaded_images.append({
                'path': path,
                'image': img,
                'size': img.size,
                'mode': img.mode
            })
        except Exception as e:
            print(f"読み込みエラー {path}: {e}")
            continue
    
    return loaded_images

# 使用例:すべてのJPG画像を読み込んでリサイズ
images = load_images_batch('photos/**/*.jpg', target_size=(224, 224))
print(f"読み込み成功: {len(images)}枚")

# 画像情報の表示
for img_data in images[:5]:  # 最初の5枚のみ表示
    print(f"{img_data['path']}: {img_data['size']}, {img_data['mode']}")

このように、Python globを活用することで様々なファイル操作タスクを効率化できます。ただし、大量のファイルを扱う場合はメモリ使用量に注意が必要です。次のセクションで紹介するiglob()関数を使えば、メモリ効率の良い処理が可能になります。

“`

“`html

iglob()関数によるイテレータ処理

python+files+iterator

Pythonのglobモジュールには、glob()関数に加えてiglob()関数が用意されています。iglob()関数は、マッチしたファイルパスをイテレータとして返す特性を持ち、大量のファイルを扱う際のメモリ効率を大幅に向上させることができます。特に数千、数万単位のファイルを処理する場合には、iglob()関数の利用が推奨されます。

iglob()とglob()の違い

glob()関数とiglob()関数の最も大きな違いは、戻り値の形式にあります。glob()関数はマッチしたすべてのファイルパスをリストとして一度にメモリ上に展開しますが、iglob()関数はイテレータオブジェクトを返し、必要に応じて一つずつファイルパスを生成します。

import glob

# glob()関数の場合:すべての結果をリストで返す
files_list = glob.glob('*.txt')
print(type(files_list))  # <class 'list'>
print(files_list)  # ['file1.txt', 'file2.txt', 'file3.txt']

# iglob()関数の場合:イテレータを返す
files_iterator = glob.iglob('*.txt')
print(type(files_iterator))  # <class 'generator'>

# イテレータから一つずつ取得
for file in files_iterator:
    print(file)

この違いにより、iglob()関数は以下のような特徴を持ちます:

  • 遅延評価:必要になったタイミングでファイルパスを生成するため、初期化が高速
  • メモリ効率:すべての結果を同時にメモリに保持しないため、省メモリ
  • ストリーミング処理:for文などで順次処理する場合に最適
  • 一度きりの走査:イテレータは一度しか走査できないため、再利用には注意が必要
比較項目 glob() iglob()
戻り値の型 list iterator (generator)
メモリ使用量 ファイル数に比例して増加 ほぼ一定
初期化速度 全件検索後に返却 即座に返却
再利用 可能 不可(一度きり)
インデックスアクセス 可能 不可

メモリ効率を考慮した大量ファイルの処理

大量のファイルを扱うシステムでは、iglob()関数を活用することでメモリ不足を回避し、安定した処理を実現できます。例えば、ログファイルの解析やデータセットの処理など、数万件のファイルを扱う場合に特に効果を発揮します。

import glob
import os

# 大量のファイルを効率的に処理する例
file_count = 0
total_size = 0

# iglob()を使用してメモリ効率的に処理
for filepath in glob.iglob('**/*.log', recursive=True):
    file_count += 1
    total_size += os.path.getsize(filepath)
    
    # ファイルを一つずつ処理
    with open(filepath, 'r') as f:
        # 処理内容(例:エラーログの抽出)
        for line in f:
            if 'ERROR' in line:
                print(f'{filepath}: {line.strip()}')

print(f'処理ファイル数: {file_count}')
print(f'合計サイズ: {total_size / (1024**2):.2f} MB')

このコードでは、recursive=Trueを指定して再帰的にログファイルを検索していますが、glob()関数を使用すると全ファイルパスのリストがメモリ上に展開されるため、ファイル数が多い場合はメモリ不足になる可能性があります。一方、iglob()関数を使用すれば、一つずつファイルを処理するため安全です。

より実践的な活用例として、条件に合致したファイルのみをカウントする処理を見てみましょう:

import glob
import os
from datetime import datetime, timedelta

# 過去7日間に更新されたPythonファイルを効率的に検索
week_ago = datetime.now() - timedelta(days=7)
recent_files = []

for filepath in glob.iglob('**/*.py', recursive=True):
    # ファイルの更新日時を確認
    mtime = datetime.fromtimestamp(os.path.getmtime(filepath))
    
    if mtime > week_ago:
        recent_files.append(filepath)
        
        # 一定数に達したら途中で処理を中断することも可能
        if len(recent_files) >= 100:
            print('100件に達したため処理を中断します')
            break

print(f'最近更新されたファイル: {len(recent_files)}件')

iglob()関数を使用する際の注意点として、以下を押さえておきましょう:

  • イテレータの使い捨て性:一度forループで走査したイテレータは再利用できないため、複数回使用する場合はlist()で変換するか、再度iglob()を呼び出す
  • 途中中断の効率性:特定の条件でbreakする場合、iglob()なら不要なファイル検索を省略できる
  • メモリとCPUのトレードオフ:ファイル数が少ない場合はglob()の方がシンプルで高速な場合もある
  • 並列処理との相性:イテレータは並列処理には向かないため、マルチスレッド処理する場合はlist化が必要
# イテレータをリスト化して再利用する例
import glob

# イテレータを取得
file_iterator = glob.iglob('data/*.csv')

# 必要に応じてリスト化
file_list = list(file_iterator)

# これで複数回使用可能
print(f'ファイル数: {len(file_list)}')
for file in file_list:
    print(file)

このように、iglob()関数は大量ファイルの処理において非常に有効なツールです。処理するファイル数やメモリの制約を考慮して、glob()関数とiglob()関数を適切に使い分けることで、効率的なPythonプログラムを実現できます。

“`

“`html

globを使ったファイル操作の実例

python+file+management

globモジュールは単にファイルを検索するだけでなく、実際のファイル操作と組み合わせることで真価を発揮します。ここでは、実務で頻繁に使用される具体的なファイル操作のパターンを解説します。検索したファイルに対して一括処理を行う方法を理解することで、手作業では膨大な時間がかかる作業を数行のコードで効率化できるようになります。

条件に合致したファイルへの一括処理

globで取得したファイルリストに対して、繰り返し処理を組み合わせることで様々な一括操作が可能になります。例えば、特定の拡張子を持つすべてのファイルに対して内容の変更や統計情報の取得などを行うことができます。

以下は、テキストファイルの文字数をカウントする実例です。

import glob

# すべてのテキストファイルを取得
txt_files = glob.glob("data/*.txt")

# 各ファイルの文字数をカウント
for file_path in txt_files:
    with open(file_path, 'r', encoding='utf-8') as f:
        content = f.read()
        char_count = len(content)
        print(f"{file_path}: {char_count}文字")

また、ファイル名の一括変更も頻繁に行われる操作です。日付やプレフィックスを追加する場合の例を示します。

import glob
import os
from datetime import datetime

# すべてのPDFファイルを取得
pdf_files = glob.glob("documents/*.pdf")

# ファイル名に日付を追加
date_prefix = datetime.now().strftime("%Y%m%d")

for file_path in pdf_files:
    dir_name = os.path.dirname(file_path)
    base_name = os.path.basename(file_path)
    new_name = f"{date_prefix}_{base_name}"
    new_path = os.path.join(dir_name, new_name)
    
    os.rename(file_path, new_path)
    print(f"リネーム完了: {base_name} → {new_name}")

条件分岐を組み合わせることで、より柔軟な処理も可能です。例えば、ファイルサイズが一定以上のファイルのみを対象にする、作成日時が特定期間のファイルのみを処理するなど、実務で求められる複雑な要件にも対応できます。

ファイルの一括削除方法

不要なファイルを整理する際、globモジュールとosモジュールを組み合わせてファイルの一括削除を行うことができます。ただし、削除操作は取り消しができないため、実行前に必ず対象ファイルを確認することが重要です。

安全に一括削除を実行する基本的な手順を以下に示します。

import glob
import os

# 削除対象のファイルを取得(一時ファイルを想定)
temp_files = glob.glob("temp/*.tmp")

# 削除前に対象ファイルを確認
print("以下のファイルを削除します:")
for file_path in temp_files:
    print(f"  - {file_path}")

# 確認プロンプト(実際の運用では実装推奨)
confirm = input("削除を実行しますか? (yes/no): ")

if confirm.lower() == 'yes':
    for file_path in temp_files:
        try:
            os.remove(file_path)
            print(f"削除完了: {file_path}")
        except Exception as e:
            print(f"削除失敗: {file_path} - {e}")
else:
    print("削除をキャンセルしました")

より安全な削除方法として、条件を組み合わせた削除も実装できます。例えば、30日以上前に作成されたログファイルのみを削除する場合は次のようになります。

import glob
import os
import time

# ログファイルを取得
log_files = glob.glob("logs/*.log")

# 現在時刻
current_time = time.time()
# 30日前のタイムスタンプ
thirty_days_ago = current_time - (30 * 24 * 60 * 60)

for file_path in log_files:
    # ファイルの最終更新時刻を取得
    file_mtime = os.path.getmtime(file_path)
    
    # 30日以上古いファイルのみ削除
    if file_mtime  thirty_days_ago:
        os.remove(file_path)
        print(f"古いログファイルを削除: {file_path}")

本番環境で削除スクリプトを実行する際は、必ずバックアップを取得し、テスト環境で動作確認を行うことをお勧めします。

データの読み込みと表示処理

globモジュールは、複数のデータファイルを効率的に読み込む処理でも活躍します。特にCSVファイルやJSONファイルなど、複数に分割されたデータセットを一括で処理する場合に便利です。

CSVファイルを一括で読み込み、データを統合する例を示します。

import glob
import csv

# すべてのCSVファイルを取得
csv_files = glob.glob("data/*.csv")

# データを格納するリスト
all_data = []

# 各CSVファイルを読み込み
for file_path in csv_files:
    with open(file_path, 'r', encoding='utf-8') as f:
        reader = csv.DictReader(f)
        for row in reader:
            all_data.append(row)
    print(f"読み込み完了: {file_path}")

# 統合データの件数を表示
print(f"\n合計データ件数: {len(all_data)}件")

# 最初の3件を表示
print("\nサンプルデータ:")
for i, record in enumerate(all_data[:3], 1):
    print(f"{i}. {record}")

画像ファイルの一覧表示やサムネイル生成など、視覚的なデータを扱う場合の実例も示します。

import glob
from PIL import Image
import os

# 画像ファイルを取得
image_files = glob.glob("images/*.{jpg,png}", recursive=False)
# 複数拡張子を扱う場合
image_files = []
for ext in ['*.jpg', '*.jpeg', '*.png', '*.gif']:
    image_files.extend(glob.glob(f"images/{ext}"))

# 各画像の情報を表示
print("画像ファイル一覧:")
print("-" * 60)

for file_path in image_files:
    try:
        with Image.open(file_path) as img:
            width, height = img.size
            file_size = os.path.getsize(file_path)
            file_name = os.path.basename(file_path)
            
            print(f"ファイル名: {file_name}")
            print(f"  サイズ: {width}x{height}px")
            print(f"  容量: {file_size / 1024:.2f}KB")
            print("-" * 60)
    except Exception as e:
        print(f"エラー: {file_path} - {e}")

さらに、pandasライブラリと組み合わせることで、より高度なデータ分析も可能になります。

import glob
import pandas as pd

# すべてのCSVファイルを取得
csv_files = glob.glob("sales_data/*.csv")

# データフレームのリスト
df_list = []

# 各ファイルを読み込み
for file_path in csv_files:
    df = pd.read_csv(file_path)
    df_list.append(df)
    print(f"読み込み: {file_path} ({len(df)}行)")

# すべてのデータフレームを結合
combined_df = pd.concat(df_list, ignore_index=True)

# 統計情報を表示
print("\n統合データの統計情報:")
print(combined_df.describe())

# データをExcelファイルとして保存
combined_df.to_excel("combined_sales_data.xlsx", index=False)
print("\n統合データを保存しました: combined_sales_data.xlsx")

このように、globモジュールを使ったファイル操作は、データ処理の自動化において非常に強力なツールとなります。適切なエラーハンドリングとログ出力を組み合わせることで、安全で効率的なファイル操作システムを構築できます。

“`

“`html

globモジュール活用時の注意点とまとめ

python+file+search

Pythonのglobモジュールは、ファイルやディレクトリの検索を簡単に実行できる便利なツールですが、実際の開発現場で活用する際にはいくつかの注意点があります。これらを理解しておくことで、予期しないエラーや非効率な処理を避けることができます。

パス区切り文字の扱いに関する注意

globモジュールを使用する際、パス区切り文字は常にスラッシュ(/)を使用することが推奨されます。Windowsではバックスラッシュ(\)がパス区切り文字として使われますが、globではスラッシュを使用することでクロスプラットフォームでの互換性が保たれます。

# 推奨される書き方
files = glob.glob("data/folder/*.txt")

# Windows環境でも動作するが推奨されない
files = glob.glob("data\\folder\\*.txt")

検索結果の順序に関する注意

globモジュールで取得したファイルリストは順不同であり、特定の順序を保証しません。ファイルシステムの実装に依存するため、実行環境によって順序が異なる可能性があります。ファイル名順やタイムスタンプ順に処理したい場合は、sorted()関数を併用する必要があります。

# アルファベット順にソート
files = sorted(glob.glob("*.txt"))

# 更新日時順にソート
files = sorted(glob.glob("*.txt"), key=os.path.getmtime)

大量ファイル処理時のメモリ効率

数千から数万を超える大量のファイルを扱う場合、glob()関数は全ての結果をリストとして一度にメモリに読み込むため、メモリ使用量が大きくなります。このような状況では、iglob()関数を使用してイテレータ処理を行うことでメモリ効率を改善できます

# メモリ効率の良い処理
for filepath in glob.iglob("**/*.log", recursive=True):
    # ファイルを1つずつ処理
    process_file(filepath)

シンボリックリンクの扱い

globモジュールは、デフォルトでシンボリックリンクをたどります。これにより、循環参照が存在する環境では無限ループに陥る可能性があります。特にrecursive=Trueを使用した再帰検索では注意が必要です。

特殊文字を含むファイル名の検索

ファイル名に角括弧([])やアスタリスク(*)などの特殊文字が含まれている場合、これらはワイルドカードとして解釈されてしまいます。実際の文字として検索するには、glob.escape()関数を使用してエスケープ処理を行う必要があります。

# 特殊文字を含むファイル名を検索
filename = "data[1].txt"
escaped = glob.escape(filename)
files = glob.glob(escaped)

隠しファイルの扱い

Unix系システムでは、ドット(.)で始まるファイルは隠しファイルとして扱われます。通常のワイルドカード検索では隠しファイルは取得されません。Python 3.11以降ではinclude_hidden引数を使用することで、隠しファイルも含めた検索が可能になりました。

相対パスと絶対パスの使い分け

globモジュールは、指定したパターンに応じて相対パスまたは絶対パスで結果を返します。パターンが相対パスの場合は相対パスで、絶対パスの場合は絶対パスで結果が返されます。ファイル操作を行う際は、この動作を理解しておくことが重要です。

# 相対パスで取得
files = glob.glob("*.txt")  # ['file1.txt', 'file2.txt']

# 絶対パスで取得
files = glob.glob("/home/user/*.txt")  # ['/home/user/file1.txt', ...]

パフォーマンスに関する考慮事項

深い階層構造を持つディレクトリでrecursive検索を行うと、処理時間が大幅に増加する可能性があります。できるだけ具体的なパスパターンを指定し、検索範囲を限定することでパフォーマンスを改善できます。

まとめ

Pythonのglobモジュールは、ファイル検索を効率的に実行できる強力なツールです。基本的なワイルドカード検索から再帰的な階層検索まで、様々な用途に対応できます。ただし、検索結果の順序保証がない点、大量ファイル処理時のメモリ使用量、特殊文字のエスケープ処理など、いくつかの注意点を理解しておく必要があります。

適切な関数選択(glob()とiglob()の使い分け)と、パターンマッチングの正確な記述により、安全で効率的なファイル操作が実現できます。本記事で紹介した基本的な使い方から応用テクニックまでを活用することで、ファイル処理のコードをより簡潔で保守性の高いものにすることができるでしょう。

注意点 対策
検索結果の順序が不定 sorted()関数でソート処理を実施
大量ファイルのメモリ消費 iglob()でイテレータ処理を利用
特殊文字を含むファイル名 glob.escape()でエスケープ処理
隠しファイルが取得されない include_hidden引数を使用(Python 3.11+)
深い階層の検索で時間がかかる 具体的なパターンで検索範囲を限定

“`