3行で
- 手軽にやるなら
glob.glob
- サブディレクトリまで走査するなら、python 3.4以前なら
os.walk
、python 3.5以降ならglob.glob
- python 3.4以降で、その後のファイル操作まで考えるなら、
pathlib
がお勧め
前置き
大量のファイルを一括にリネームする必要があったので、調べたことを忘備録がわりに書いておきます。
あるディレクトリに入っているファイルの一覧を取得します。応用として、特定の条件にマッチするファイルのみ抽出したり、サブディレクトリ内部のファイルまで再帰的に取得することもできます。なお、この例では以下のようなディレクトリ構成になっているとします。
/test_dir
└── dir_A
├── hoge.txt
├── huga.txt
├── nyaaan.JPG
└── dir_B
└── piyo.txt
確認環境:
- Windows7 Professional
- Python 3.5.3, 2.7.13
基本
glob.glob
関数でパスを取得できます。
import glob
glob.glob("/test_dir/dir_A/*")
# Windowsでは'\\'でもディレクトリを区切れます
glob.glob("\\test_dir\\dir_A\\*")
# 相対パスでも指定可能です
import os
os.chdir("/test_dir/dir_A")
glob.glob("./*")
# 絶対パスで指定した場合は絶対パスが返る
['/test_dir/dir_A\\dir_B',
'/test_dir/dir_A\\hoge.txt',
'/test_dir/dir_A\\huga.txt',
'/test_dir/dir_A\\nyaaan.JPG']
['.\\dir_B', '.\\hoge.txt', '.\\huga.txt', '.\\nyaaan.JPG'] # 相対パスで指定した場合は相対パスが返る
取得した相対パスはos.path.abspath
関数により絶対パスへ変換できます。
ファイルの件数が多くて時間がかかる場合は、yield
でジェネレータにするとマシになるかもしれません。
def listup_files(path):
yield [os.path.abspath(p) for p in glob.glob(path)]
条件指定
ワイルドカードにより検索の条件指定ができます。glob.glob
関数では'*', '?', '[]'のワイルドカードが指定できます。それぞれの役割は以下です。'*'についてはちゃっかり上で使っていますね。
- '*': 任意の文字列とマッチする(長さ0文字~)
- '?': 任意の1文字とマッチする
- '[]': [xxx-yyy]内の範囲に含まれる文字にマッチする。例えば[0-9]とすると任意の数字1文字が指定できます。
import glob
# テキストファイルのみを取得
glob.glob("/test_dir/dir_A/*.txt")
# "n"で始まるファイル,ディレクトリのみを取得
glob.glob("/test_dir/dir_A/n*")
# ディレクトリ名にワイルドカードを指定することも可能。
glob.glob("/test_dir/dir_A/*/*.txt")
['/test_dir/dir_A\\hoge.txt', '/test_dir/dir_A\\huga.txt'] # テキストファイルのみ
['/test_dir/dir_A\\nyaaan.JPG'] # ”n”で始まる
['/test_dir/dir_A\\dir_B\\piyo.txt'] # ディレクトリ名にワイルドカード。dir_A直下のテキストは含まれていません。
ただし、ワイルドカードで「xxxを含まない」といった指定はできないようです。したがって、「含まない」パターンを指定したいときは
- 取得したリストに対し、さらに正規表現でパターンマッチ
- すべてのファイルのリストと、除きたいファイルのみのリストをそれぞれ用意して、set型などで差分をとる
などの工夫が必要になります。
サブディレクトリまで再帰的に含める
python3.5以降のバージョンでは、glob.glob
関数で再帰的な検索ができます。
それ以前のバージョンでは、os.walk
関数を使います。
# python 3.5以降
glob.glob("/test_dir/**.txt", recursive=True)
# python3.4以前
found = []
for root, dirs, files in os.walk("/test_dir/"):
for filename in files:
found.append(os.path.join(root, filename)) # ファイルのみ再帰でいい場合はここまででOK
for dirname in dirs:
found.append(os.path.join(root, dirname)) # サブディレクトリまでリストに含めたい場合はこれも書く
print(found)
# glob.globとos.walk どちらも同じ
['/test_dir\\',
'/test_dir\\dir_A',
'/test_dir\\dir_A\\dir_B',
'/test_dir\\dir_A\\dir_B\\piyo.txt',
'/test_dir\\dir_A\\hoge.txt',
'/test_dir\\dir_A\\huga.txt',
'/test_dir\\dir_A\\nyaaan.JPG']
pathlib.Pathを使う
python 3.4以降ではpathlib
モジュールが標準ライブラリに追加されました。
このモジュールで定義されているPath
クラスでもファイルの検索ができます。
Pathクラスはファイルパスに関する操作APIが充実していて、パスの文字列をそのまま扱うよりも使いやすいです。python 3.4以降の環境であれば、上記の方法よりもこちらをお勧めします。
from pathlib import Path
# Pathオブジェクトを生成
p = Path("/test_dir/dir_A/")
# dir_A直下のファイルとディレクトリを取得
# Path.glob(pattern)はジェネレータを返す。結果を明示するためlist化しているが、普段は不要。
list(p.glob("*"))
# ファイル名の条件指定
list(p.glob("*.txt"))
# 再帰的な検索
list(p.glob("**/*"))
# dir_A直下のファイルとディレクトリ
[WindowsPath('/test_dir/dir_A/dir_B'),
WindowsPath('/test_dir/dir_A/hoge.txt'),
WindowsPath('/test_dir/dir_A/huga.txt'),
WindowsPath('/test_dir/dir_A/nyaaan.JPG')]
# 条件指定
[WindowsPath('/test_dir/dir_A/hoge.txt'),
WindowsPath('/test_dir/dir_A/huga.txt')]
# 再帰的
[WindowsPath('/test_dir/dir_A/dir_B'),
WindowsPath('/test_dir/dir_A/hoge.txt'),
WindowsPath('/test_dir/dir_A/huga.txt'),
WindowsPath('/test_dir/dir_A/nyaaan.JPG'),
WindowsPath('/test_dir/dir_A/dir_B/piyo.txt')]
WindowsPath
はPath
のサブクラスです。MacやLinux系の環境では結果が変わるはずです。
open
などpython標準で入っている関数には、文字列の代わりにPathオブジェクトをそのまま渡せます。
一方で、外部ライブラリのなかにはPathを受け付けないものもあります。軽く調べたところ、numpy.loadtxt(Path_obj)
やpandas.read_csv(Path_obj)
は正常にファイルを開けましたが、cv2.imread(Path_obj)
はエラーになりました。
Pathオブジェクトを通常の文字列に変換するには、Path.as_posix
関数を使います。あるいは、str(Path)
のようにキャストしてもよいです。
from pathlib import Path
import numpy as np
import pandas as pd
import cv2
# numpyはPathオブジェクトを受け付ける
csv = Path("./sample.csv")
np.loadtxt(csv, delimiter=",")
# pandasもPathオブジェクトを受け付ける
pd.read_csv(csv)
# OpenCVはエラーになる
jpg = Path("/test_dir/dir_A/nyaaan.JPG")
cv2.imread(jpg)
# 文字列として渡せばOK
cv2.imread(jpg.as_posix())
# numpy -> 正常にファイルを読み込む(略)
# pandas -> 正常にファイルを読み込む(略)
# opencv(Path_obj)
TypeError Traceback (most recent call last)
<ipython-input-4-54c7c3460c88> in <module>()
----> 1 cv2.imread(jpg)
TypeError: bad argument type for built-in operation
# opencv(Path_obj.as_posix()) -> 正常にファイルを読み込む(略)
私信
qiitaへの投稿は初めてです。内容やレイアウトなどのツッコミをいただけると助かります。どうぞご遠慮なさらずに。
参考資料
[Python 3.6.3ドキュメント 16.1. os] (https://docs.python.jp/3/library/os.html)
[python 3.6.3ドキュメント 11.1 pathlib] (https://docs.python.jp/3/library/pathlib.html)
[Pythonで再帰的にファイル・ディレクトリを探して出力する] (https://qiita.com/suin/items/cdef17e447ceeff6e79d)
[Python3.4以降ならos.pathはさっさと捨ててpathlibを使うべき] (https://qiita.com/tag1216/items/29bf3b9c3c4b563ff0fe)