【Python】ファイルやディレクトリの一覧を取得する
特定のディレクトリの直下にあるファイルやディレクトリの一覧や、特定のディレクトリの配下にある全てのファイル・ディレクトリを取得する方法について説明します。
大きく分けて2種類の方法があります。
- osモジュールを使う方法
- pathlibライブラリを使う方法
基本的にはpathlibを使うので良いと思いますが、本記事ではosモジュールを使う方法とpathlibを使う方法の両方で説明していきます。
osとpathlibの違い
osモジュールもpathlibもファイルやディレクトリに対して似たような操作ができます。
osの方が歴史が古く(2系から存在する)、pathlibは新しい(3.4以降)です。
osモジュールの特徴
osモジュールはPythonの標準ライブラリの一つで、文字通りOSの機能にアクセスするためのモジュールです。
パス(ファイルやディレクトリ)について扱う場合はos.pathサブモジュールを使いますが、本記事のようにファイル一覧などを取得したい場合はos.pathは使いません。
基本的には全て文字列で扱うことになります。そのため、パスの操作や組み立てが少し複雑になることもあります。
「/」が多い少ないなどは、慣れてないとやりがちなミスです。
pathlibライブラリの特徴
パスをオブジェクト指向的に扱うためのライブラリで、パスの操作を直感的に行うことが可能です。
また、パスをPathオブジェクトとして扱うのでメソッドチェーンも可能です。
新規プロジェクトで開発する場合は、基本的にはpathlibライブラリを使う方が、可読性の高いコードが書けるので良いと思います。
ディレクトリ直下にあるディレクトリ一覧を取得する
osモジュールを使う方法
os.listdir()
で引数で指定したディレクトリ直下のファイルやディレクトリをすべて取得し、そこからディレクトリだけを抽出します。
1import os
2
3# 特定のディレクトリを指定する
4dir_path = "./my_parent_dir"
5
6# os.listdir()関数を使ってディレクトリ内の全てのファイルやディレクトリ名を取得する
7names = os.listdir(dir_path)
8print(names)
9# ['my_dir1', 'my_dir2', 'my_file1.txt', 'my_file2.csv']
10
11# ディレクトリ名だけを抽出する
12dir_names = [name for name in names if os.path.isdir(os.path.join(dir_path, name))]
13
14# ディレクトリ名の一覧を表示するで
15print(dir_names)
16# ['my_dir1', 'my_dir2']
pathlibを使う方法
Pathオブジェクトのitterdir()
でそのパスの直下にある全てのファイルやディレクトリを取得し、そこからディレクトリのみに絞り込みます。
1from pathlib import Path
2
3# 特定のディレクトリを指定する
4dir_path = Path("./my_parent_dir")
5
6# iterdir()メソッドを使ってディレクトリ内の全てのファイルやディレクトリをイテレートする
7dir_names = [p.name for p in dir_path.iterdir() if p.is_dir()]
8
9# ディレクトリ名の一覧を表示するで
10print(dir_names)
11# ['my_dir1', 'my_dir2']
リスト内法表記でdir_names
に要素を入れる時にp.name
としているのでディレクトリ名だけがdir_names
に入りますが、p
とすれば、親ディレクトリも含めたパスとしてdir_names
に入れることが可能です。
サブディレクトリも含めて再帰的にディレクトリ一覧を取得する
osを使う方法
os.walk()を使うことで、引数で指定したディレクトリの配下のファイルやディレクトリを再帰的に探索することが可能です。
1import os
2
3# 親ディレクトリを指定する
4dir_path = "./my_parent_dir"
5
6# os.walk()を使ってディレクトリを再帰的に探索する
7for root, dirs, files in os.walk(dir_path):
8 print(f"現在のディレクトリ: {root}")
9 print(f" ファイル一覧:{files}")
10 print(f" サブディレクトリ一覧:{dirs}")
11
12# 現在のディレクトリ: ./my_parent_dir
13# ファイル一覧:['my_file1.txt', 'my_file2.csv']
14# サブディレクトリ一覧:['my_dir1', 'my_dir2']
15# 現在のディレクトリ: ./my_parent_dir\my_dir1
16# ファイル一覧:['my_child_file1.csv', 'my_child_file1.txt']
17# サブディレクトリ一覧:['my_child_dir1', 'my_child_dir2']
18# 現在のディレクトリ: ./my_parent_dir\my_dir1\my_child_dir1
19# ファイル一覧:[]
20# サブディレクトリ一覧:[]
21# 現在のディレクトリ: ./my_parent_dir\my_dir1\my_child_dir2
22# ファイル一覧:[]
23# サブディレクトリ一覧:[]
24# 現在のディレクトリ: ./my_parent_dir\my_dir2
25# ファイル一覧:[]
26# サブディレクトリ一覧:[]
現在のディレクトリ(root)に対してファイルとディレクトリをそれぞれ取得していきます。
そのため、ディレクトリの数だけループすることになります。
ファイル一覧などを作成する場合、rootがどこなのかを意識しておく必要があります。
また、区切り文字にも注意が必要です。戻り値は基本的に文字列で、「/」と「\」などは別の文字なので、文字列操作を行う場合は注意が必要です。
pathlibを使う方法
Pathオブジェクトはrglob()で再帰的にディレクトリを探索できます。
Pathオブジェクトのリストが返ってきます。
1from pathlib import Path
2
3# 親ディレクトリを指定する
4dir_path = Path("./my_parent_dir")
5
6# rglob()を使ってディレクトリを再帰的に探索する
7for p in dir_path.rglob("*"):
8 if p.is_dir():
9 print(f"ディレクトリ: {p}")
10# ディレクトリ: my_parent_dir\my_dir1
11# ディレクトリ: my_parent_dir\my_dir2
12# ディレクトリ: my_parent_dir\my_dir1\my_child_dir1
13# ディレクトリ: my_parent_dir\my_dir1\my_child_dir2
リスト内法表記にすると以下のようになります。
1from pathlib import Path
2import pprint
3
4# 親ディレクトリを指定する
5dir_path = Path("./my_parent_dir")
6
7# rglob()を使ってディレクトリを再帰的に探索する
8dirs = [p for p in dir_path.rglob("*") if p.is_dir()]
9pprint.pprint(f"{dirs}")
10# ("[WindowsPath('my_parent_dir/my_dir1'), WindowsPath('my_parent_dir/my_dir2'), "
11# "WindowsPath('my_parent_dir/my_dir1/my_child_dir1'), "
12# "WindowsPath('my_parent_dir/my_dir1/my_child_dir2')]")
Pathオブジェクトとして取得できるので、その後の操作もやりやすいことが多いと思います。
ディレクトリ直下にあるファイル一覧を取得する
基本的な方法としては、ディレクトリ一覧を取得する場合と同じです。
まずは、直下にあるファイルやディレクトリの一覧を取得し、そこからファイルかどうかの判断をしていく流れになります。
osを利用する方法
os.listdir()
でディレクトリ直下の全ての名前(ファイル名やディレクトリ名)を取得し、os.path.join()
で親ディレクトリと結合し、os.path.isfile()
でそれがファイルかどうかを判断しています。
1import os
2
3# 特定のディレクトリを指定する
4dir_path = "my_parent_dir/"
5
6# os.listdir()でディレクトリ内の全ての名前を取得する
7names = os.listdir(dir_path)
8
9# ファイル名だけを抽出する
10file_names = [name for name in names if os.path.isfile(os.path.join(dir_path, name))]
11
12# ファイル名の一覧を表示する
13print(file_names)
14# ['my_file1.txt', 'my_file2.csv']
os.path.isfile(os.path.join(dir_path, name))
このように行数を減らそうと思うと、関数が入れ子になりやすいです。入れ子を減らすには、変数を増やすしかないですが、一度しか使わない変数だと、わざわざ変数に入れたくないので悩ましい所です。
pathlibを使う方法
Pathオブジェクトのiterdir()
でディレクトリ直下のファイルやディレクトリを取得し、is_file()
でファイルかどうか判断します。
1from pathlib import Path
2
3# 特定のディレクトリを指定する
4dir_path = Path("my_parent_dir/")
5
6# iterdir()でディレクトリ内の全てのパスをイテレートする
7file_names = [p.name for p in dir_path.iterdir() if p.is_file()]
8
9# ファイル名の一覧を表示する
10print(file_names)
11# ['my_file1.txt', 'my_file2.csv']
osモジュールを使うよりもすっきりと書けることが多いです。
サブディレクトリを含む全てのファイル名を再帰的に取得する
osを利用する方法
os.walk()で親ディレクトリの配下にある全てのディレクトリからファイル名やディレクトリ名を取得し、ファイル名と親ディレクトリからのパスにした文字列を取得する例です。
1import os
2
3# 親ディレクトリを指定する
4dir_path = "./my_parent_dir/"
5
6# os.walk()を使って再帰的に探索する
7for root, dirs, files in os.walk(dir_path):
8 for file_name in files:
9 print(file_name)
10 # 親ディレクトリからの絶対パスを作る
11 abs_path = os.path.join(root, file_name)
12 print(abs_path)
13
14# my_file1.txt
15# ./my_parent_dir/my_file1.txt
16# my_file2.csv
17# ./my_parent_dir/my_file2.csv
18# my_child_file1.csv
19# ./my_parent_dir/my_dir1\my_child_file1.csv
20# my_child_file1.txt
21# ./my_parent_dir/my_dir1\my_child_file1.txt
ファイルがないディレクトリの場合、filesのループが実行されないので、ファイルの存在するディレクトリのみに処理を行えます。
pathlibを利用する方法
rglob()で再帰的に探索し、is_file()でファイルかを判断します。
1from pathlib import Path
2
3# 親ディレクトリを指定する
4dir_path = Path("my_parent_dir/")
5
6# rglob()で再帰的にファイルを探索する
7for p in dir_path.rglob('*'):
8 if p.is_file():
9 print(p)
10
11# my_parent_dir\my_file1.txt
12# my_parent_dir\my_file2.csv
13# my_parent_dir\my_dir1\my_child_file1.csv
14# my_parent_dir\my_dir1\my_child_file1.txt
リスト内法表記で書くと以下のようになります。
1from pathlib import Path
2import pprint
3
4# 親ディレクトリを指定する
5dir_path = Path("my_parent_dir/")
6
7# rglob()をリスト内包表記で使って、ファイルのパスをリストに格納する
8file_paths = [p for p in dir_path.rglob('*') if p.is_file()]
9
10# リストを表示する
11pprint.pprint(file_paths)
12# [WindowsPath('my_parent_dir/my_file1.txt'),
13# WindowsPath('my_parent_dir/my_file2.csv'),
14# WindowsPath('my_parent_dir/my_dir1/my_child_file1.csv'),
15# WindowsPath('my_parent_dir/my_dir1/my_child_file1.txt')]
やはりPathオブジェクトの方がやりたいことを直感的にできると思います。
まとめ
1import os
2
3# 特定のディレクトリを指定する
4dir_path = "./my_parent_dir"
5
6# os.listdir()関数を使ってディレクトリ内の全てのファイルやディレクトリ名を取得する
7names = os.listdir(dir_path)
8# ディレクトリ名だけを抽出する
9dir_names = [name for name in names if os.path.isdir(os.path.join(dir_path, name))]
10# ファイル名だけを抽出する
11file_names = [name for name in names if os.path.isfile(os.path.join(dir_path, name))]
12
13# os.walk()を使ってディレクトリを再帰的に探索する
14for root, dirs, files in os.walk(dir_path):
15 print(f"現在のディレクトリ: {root}")
16 print(f" ファイル一覧:{files}")
17 print(f" サブディレクトリ一覧:{dirs}")
18
19
20from pathlib import Path
21
22# 特定のディレクトリを指定する
23dir_path = Path("./my_parent_dir")
24
25# iterdir()メソッドを使ってディレクトリ内の全てのファイルやディレクトリをイテレートする
26dir_names = [p.name for p in dir_path.iterdir() if p.is_dir()]
27# iterdir()でディレクトリ内の全てのパスをイテレートする
28file_names = [p.name for p in dir_path.iterdir() if p.is_file()]
29
30# rglob()を使ってディレクトリを再帰的に探索する
31dirs = [p for p in dir_path.rglob("*") if p.is_dir()]
32# rglob()をリスト内包表記で使って、ファイルのパスをリストに格納する
33file_paths = [p for p in dir_path.rglob('*') if p.is_file()]
34