Python
のファイル操作用のモジュールpathlib
というものがあります。
pathlib
は、一連のファイル操作を一つのモジュールでこなせる非常に強力なモジュールです。
.glob()
メソッドを使用すれば、様々な条件でのファイル・フォルダの検索を行うことが可能です。
しかし、実際にファイル検索する際には、次のような問題に直面することも:
pathlib
を使って、ファイル検索するには具体的にどうすればいいの?- 特定の条件に合ったファイルを検索する方法は?
- サブフォルダも含めて、全ファイル検索するにはどうするの?
こういった疑問を解決するために、この記事ではpathlib
の.glob()
メソッドを使用したファイル検索方法について、図解付きで解説していきます。
フォルダ内ファイル検索の基本手順
この記事では、pathlib
モジュールのPath
を使用したファイル内検索方法について紹介します。
ファイル検索の基本手順は次の通りです。
手順 | スクリプトの例 |
---|---|
1. pathlib からPath クラスをインポート |
from pathlib import Path |
2. 検索対象フォルダのPath オブジェクト作成 |
Path("検索フォルダ") |
3. 条件に合ったファイル・フォルダを検索 | Path.glob("検索条件") |
Path.glob("検索条件")
は、検索結果のジェネレータを返すので必要に応じて[リスト化]
したり、for文
を回したりします。
まずは実行例を紹介して、ファイル検索が想像以上に簡単だということをお伝えしたいと思います。
フォルダ内ファイル検索の実行例
例として、次のようなファイル構造を考えてみます。
Path.glob("検索条件")
で、base_folder内の全てのファイル、フォルダを表示させてみましょう。
# pathlibからPathクラスをインポート
from pathlib2 import Path
# 検索対象フォルダのPathオブジェクト作成
BASE_FOLDER = Path("base_folder")
# 結果をfor文でprint表示するパターン
for p in BASE_FOLDER.glob("*"):
print(p)
# base_folder\sample1.txt
# base_folder\sample2.txt
# base_folder\sub_folder1
# 結果をListにまとめるパターン
list(BASE_FOLDER.glob("*"))
# [WindowsPath('base_folder/sample1.txt'),
# WindowsPath('base_folder/sample2.txt'),
# WindowsPath('base_folder/sub_folder1')]
検索結果は、実行環境に応じたPath
クラスとして返されます。
上記の例では、「任意のファイル・フォルダ」という検索条件を"*"
という特殊文字で表現しています。
.glob()
メソッドでは、特殊文字を上手に利用して検索条件を設定することがポイントです。
次に、どのような特殊文字が使用できるのか詳しく見ていきたいと思います。
検索で使用できる特殊文字
検索で使用できる特殊文字は、次の通りです。
特殊文字 | 意味 | 適用 |
---|---|---|
* |
0文字以上の任意の文字列 | あらゆる文字(ワイルドカード) |
? |
1文字以上の任意の文字列 | |
[文字] |
[文字] 内の文字 |
例)[ab] :a or b [a-z] :a, b, c,…, z [0-9] : 0, 1, 2,…, 9 |
[!文字] |
[!文字] 内の文字以外 |
例)[!ab] :a or b 以外[!a-z] :a, b, c,…, z 以外[!0-9] : 0, 1, 2,…, 9 以外 |
** |
Path のフォルダとサブフォルダ |
** は、他の特殊文字と意味合いが異なる検索範囲を Path フォルダと、そのサブフォルダにする |
さらに追加で覚えておきたいポイントは、次の通りです。
ポイント | 適用 |
---|---|
特殊文字は、普通の文字と組合せ可 | Ex) "012_*.txt" -> 「012_file_name.txt」や「012_ファイル.txt」にマッチ |
正規表現のような 繰り返しを表す特殊文字はない |
Ex) 「01.txt」, 「12.txt」の両方にマッチするには、 -> "[0-9][0-9].txt" とする |
サブフォルダはフォルダ名/ で指定 |
Ex) sub_folder/*.txt ->「sub_folder内のtxtファイル」にのみマッチ |
特殊文字は、正規表現と類似していますが、正規表現の全ての特殊文字を使えるわけではない点に注意ですね。
検索パターンテンプレ紹介
これらのポイントを踏まえたうえで、使用頻度が高そうな検索パターンを個別に紹介していきます。
検索内容 | パターンテンプレ |
---|---|
フォルダ内の全ファイル・フォルダ | Path.glob("*") |
サブフォルダ内の全ファイル・フォルダ | Path.glob("*/*") |
サブフォルダも含めた全ファイル・フォルダ | Path.glob("**") |
特定の拡張子 | Path.glob("*.拡張子") Ex) ・ Path.glob("*.txt") ・ Path.glob("*.csv") |
特定の文字で始まる | Path.glob("特定の文字*") Ex) ・ Path.glob("2020*") ・ Path.glob("sample*") |
フォルダのみ抽出 | Path.glob("検索条件") + Path.is_dir() 組み合わせ |
ファイルのみ抽出 | Path.glob("検索条件") + Path.is_file() 組み合わせ |
これらのパターンについて、サンプルコードを紹介します。
ここでも次のようなファイル構造を想定してみます。
フォルダ内の全ファイル・フォルダ抽出
全ファイル、フォルダを抽出する場合は、検索条件に任意の文字列を意味する特殊文字"*"
を指定します。
Path.glob("*")
これは、記事冒頭で紹介したサンプルと同じですね。
特殊文字*
の検索範囲の意味をサンプルデータの例と一緒に図にしてみましょう。
サンプルデータで挙動を再確認してみましょう。
# 結果をfor文でprint表示するパターン
for p in BASE_FOLDER.glob("*"):
print(p)
# base_folder\sample1.txt
# base_folder\sample2.txt
# base_folder\sub_folder1
# 結果をListにまとめるパターン
list(BASE_FOLDER.glob("*"))
# [WindowsPath('base_folder/sample1.txt'),
# WindowsPath('base_folder/sample2.txt'),
# WindowsPath('base_folder/sub_folder1')]
検索条件の意味がわかってくると、ファイル・フォルダの検索がとても簡単なことに気づきますね。
サブフォルダ内の全ファイル・フォルダ抽出
サブフォルダ内のファイル・フォルダを抽出する方法を紹介します。
この場合は、検索条件の始めにサブフォルダを意味する*/
を追加します。
Path.glob("*/*")
検索条件*/*
の意味を、サンプルデータの例と一緒に図にまとめてみます。
サンプルデータで挙動を再確認してみましょう。
# 結果をfor文でprint表示するパターン
for p in BASE_FOLDER.glob("*/*"):
print(p)
# base_folder\sub_folder1\sample1_1.txt
# 結果をListにまとめるパターン
list(BASE_FOLDER.glob("*/*"))
# [WindowsPath('base_folder/sub_folder1/sample1_1.txt')]
"~/"
を「~の中の」と解釈すると、検索条件の意味がよくわかってきますね。
特定のフォルダを指定したい場合は、次のように使うこともできます。
"特定のフォルダ名/検索条件"
Pathフォルダ+サブフォルダで全抽出
サブフォルダも含めて、全ファイル・フォルダを検索する場合を紹介します。
この場合は、検索条件の始めに「Path
フォルダとサブフォルダ」を意味する特殊文字**
を追加します。
Path.glob("**/*")
ここで、**/*
は、次のような意味になっています。サンプルデータの例とともに図示してみます。
サンプルデータで挙動を再確認してみましょう。
# 結果をfor文でprint表示するパターン
for p in BASE_FOLDER.glob("**/*"):
print(p)
# base_folder\sample1.txt
# base_folder\sample2.txt
# base_folder\sub_folder1
# base_folder\sub_folder1\sample1_1.txt
# 結果をListにまとめるパターン
list(BASE_FOLDER.glob("**/*"))
# [WindowsPath('base_folder/sample1.txt'),
# WindowsPath('base_folder/sample2.txt'),
# WindowsPath('base_folder/sub_folder1'),
# WindowsPath('base_folder/sub_folder1/sample1_1.txt')]
**
は、正規表現にもなく、ファイル検索専用の特殊文字で、初めはとっつきにくい部分がありますが…。
「Path
フォルダとサブフォルダ」という意味だと理解すれば、使い勝手のいい強力な特殊文字ですね。
特定の拡張子で抽出
特定の拡張子のファイルを抽出する場合は、特殊文字"*"
と拡張子を組み合わせます。
Path.glob("*.拡張子")
- Ex1) txtファイルの場合 ->
Path.glob("*.txt")
- Ex2) csvファイルの場合 ->
Path.glob("*.csv")
- Ex1) txtファイルの場合 ->
ここで、"*.拡張子"
は次のような意味です。.txtの例と一緒に確認してみましょう。
これは使用頻度が高いので、サブファルダ内、Path
フォルダおよびサブフォルダ内全てを検索する場合も含めて表に整理してみます。
検索先 | パターンテンプレ |
---|---|
Pathフォルダ内 | Path.glob("*.拡張子") |
サブフォルダ内 | Path.glob("*/*.拡張子") |
Pathフォルダ+サブフォルダ内 | Path.glob("**/*.拡張子") |
サンプルデータで挙動を再確認してみましょう。
# Pathフォルダ内を検索
list(BASE_FOLDER.glob("*.txt"))
# [WindowsPath('base_folder/sample1.txt'),
# WindowsPath('base_folder/sample2.txt')]
# Pathフォルダ+サブフォルダ内を検索
list(BASE_FOLDER.glob("**/*.txt"))
# [WindowsPath('base_folder/sample1.txt'),
# WindowsPath('base_folder/sample2.txt'),
# WindowsPath('base_folder/sub_folder1/sample1_1.txt')]
特定の文字列で始まる
特定の文字列で始まるファイル・フォルダを抽出する場合は、検索対象の文字列と特殊文字"*"
を組み合わせます。
Path.glob("特定の文字*")
- Ex1)
"2020"
から始まる場合 ->Path.glob("2020*")
- Ex2)
"sample
から始まる場合 ->Path.glob("sample*")
- Ex1)
サブファルダ内、Path
フォルダおよびサブフォルダ内全てを検索する場合も含めて表に整理してみます。
検索先 | パターンテンプレ |
---|---|
Path フォルダ内 |
Path.glob("特定の文字*") |
サブフォルダ内 | Path.glob("*/特定の文字*") |
Path フォルダ+サブフォルダ内 |
Path.glob("**/特定の文字*") |
サンプルデータで挙動を再確認してみましょう。
# Pathフォルダ内を検索
list(BASE_FOLDER.glob("sample1*"))
# [WindowsPath('base_folder/sample1.txt')]
# Pathフォルダ+サブフォルダ内を検索
list(BASE_FOLDER.glob("**/sample1*"))
# [WindowsPath('base_folder/sample1.txt'),
# WindowsPath('base_folder/sub_folder1/sample1_1.txt')]
フォルダのみ・ファイルのみを抽出
フォルダのみ、ファイルのみを抽出する方法を紹介します。
Path
オブジェクトには、Path
がフォルダなのか、ファイルなのか判定するメソッドがあります。
判定内容 | メソッド名 | 結果 |
---|---|---|
フォルダかどうか | .is_dir() |
True or False |
ファイルかどうか | .is_file() |
True or False |
Path.glob()
の結果もPath
オブジェクトなので、if文
と組み合わせてフォルダ・ファイルの判定が可能です。
まずはフォルダのみを抽出する例を見てみましょう。
# 結果をfor文でprint表示するパターン
for p in BASE_FOLDER.glob("*"):
if p.is_dir():
# 処理内容
print(p)
# 実行結果
# base_folder\sub_folder1
# 結果をListにまとめるパターン
[p for p in BASE_FOLDER.glob("*") if p.is_dir()]
# [WindowsPath('base_folder/sub_folder1')]
次にファイルのみを抽出する例を見てみましょう。
# 結果をfor文でprint表示するパターン
for p in BASE_FOLDER.glob("*"):
if p.is_file():
# 処理内容
print(p)
# 実行結果
# base_folder\sample1.txt
# base_folder\sample2.txt
# 結果をListにまとめるパターン
[p for p in BASE_FOLDER.glob("*") if p.is_file()]
# [WindowsPath('base_folder/sample1.txt'),
# WindowsPath('base_folder/sample2.txt')]
ここまで覚えると、ファイル検索がかなり自由にできるようになってくると思います。
様々な条件を組み合わせて自由にファイル検索を行いましょう。
より複雑なファイル検索をしたい場合は、次に紹介する正規表現との組み合わせも選択肢の一つです。
正規表現による検索条件設定
.glob()
メソッドで使用できる特殊文字は、**
, *
, ?
, []
, [!]
のみでした。
正規表現とは異なり、繰り返しを表現する特殊文字がないところが難点です。
繰り返しを含めたより複雑な条件で検索したい場合には、素直に正規表現と組み合わせると良いです。
例えば、次のようなフォルダを考えてみましょう。
この中から1995~2000のファイルを検索したい場合は、どうしたら良いでしょうか?
正規表現を使えば簡単ですね。
import re
BASE_FOLDER2 = Path("base_folder2")
data_1995_to_2000 = []
for p in BASE_FOLDER2.glob("*.csv"):
# 正規表現でPathから数字4ケタ取得
res = re.search("\d{4}", str(p))
year = int(res.group())
# 数字4ケタが条件を満たすか判定
if 1995 <= year <=2000:
data_1995_to_2000.append(p)
print(data_1995_to_2000)
# [WindowsPath('base_folder2/1995_Data.csv'),
# WindowsPath('base_folder2/2000_Data.csv')]
この例では、ファイル数が少なかったですが、無数のファイルの中からデータ検索する場合を考えると正規表現の重要性がわかりますね。
- 【参考】
python
の正規表現については、次の記事で詳しく解説しています。
pathlibとpathlib2どっちを使う?
最後にpathlib
モジュールについて、補足事項です。
pathlib
関連のモジュールには、pathlib
とpathlib2
の2種類があります。
pathlib2
の≫ドキュメントを参考に、2つの違いを簡単にまとめると次の通りです。
モジュール | インストール要否 | モジュールメンテナンス |
---|---|---|
pathlib |
Python 標準モジュールなので不要 |
メンテナンスされていない |
pathlib2 |
pip やconda でインストール必要 |
継続してメンテナンスされている |
メンテナンスが継続されているという点から、私は基本的にpathlib2
を使用しています。
(ちなみに、conda
でいろいろモジュールをインストールしていると、いつの間にかpathlib2
がインストールされていることも多いです。)
pip
およびconda
でpathlib2
をインストール際のコマンドは次の通りです。
pip install pathlib2
conda install pathlib2
Pythonで作業を効率化する方法
今回はpathlib
の.glob()
メソッドでファイル検索する方法を解説しました。
Python
を使えば、簡単なスクリプトで作業を劇的に効率化することも可能です!
Python
の効率化関係で言えば、次の記事で紹介しているような本が自動化の本がおススメです。