徳島ゲーム開発ごっこ 技術ブログ

ゲームを作るために役に立ったり立たなかったりする技術を学んでいきます!

【Python】glob を使ってファイルやディレクトリを検索する

Pythonglob モジュールの使い方に関するメモ書き。
指定した名前のファイルやディレクトリを検索して、その一覧を取得したいときに使いますー。
f:id:urahimono:20200209162128p:plain


この記事にはPython3.7.4を使用しています。

今回扱うディレクトリ構成について

ファイルの検索を行うわけなので、テストした際のディレクトリ構成もメモっておかないと、何が検索対象で何が検索対象外なのか分からなくなるので、ディレクトリ構成も書いておきます。

C
└── Root ← カレントディレクトリ
   ├── Example.py
   ├── abc.txt
   ├── 123.txt
   ├── [].txt
   ├── [0].txt
   ├── dir0
   │  └── defg.txt
   └── dir1
      ├── 456.txt
      └── dir2
         └── 7890.txt

Cドライブの下にRootディレクトリがあって、そこをカレントディレクトリとして作業してます。

glob を使ってみよう

ではさっそく使っていきましょう。
globimport して、glob()関数を呼ぶ。
引数には何も考えずに * を指定する。
そうすればカレントディレクトリ内のファイルやディレクトリが一覧で返ってくる。

Example.py

import glob

files = glob.glob('*')
print(files)

['123.txt', 'abc.txt', 'dir0', 'dir1', 'Example.py', '[0].txt', '[].txt']

C
└── Root
   ├── Example.py
   ├── abc.txt
   ├── 123.txt
   ├── [].txt
   ├── [0].txt
   ├── dir0
   │  └── defg.txt
   └── dir1
      ├── 456.txt
      └── dir2
         └── 7890.txt

あら、簡単!
返ってきたパスは相対パスのようですね。
絶対パスの形で引数を渡すとどうなるのでしょうか。
Example.py

import glob
import os

currentDir = os.getcwd()
files = glob.glob(os.path.join(currentDir, '*'))
print(files)

['C:\Root\123.txt', 'C:\Root\abc.txt', 'C:\Root\dir0', 'C:\Root\dir1', 'C:\Root\Example.py', 'C:\Root\[0].txt', 'C:\Root\[].txt']

C
└── Root
   ├── Example.py
   ├── abc.txt
   ├── 123.txt
   ├── [].txt
   ├── [0].txt
   ├── dir0
   │  └── defg.txt
   └── dir1
      ├── 456.txt
      └── dir2
         └── 7890.txt

今度は絶対パスで返ってきましたね。
相対パスを引数で渡すと、返ってくるパスのリストは相対パス
絶対パスを引数で渡すと、返ってくるパスのリストは絶対パス
みたいですねー。

サブディレクトリ内も再帰的に調べたいな

カレントディレクトリ内のファイルは取得できましたが、サブディレクトリ内の中身も一緒に欲しい時だってあるはずです。
さて、どうしたものでしょう。
えーと、ドキュメントには……っと。
引数に何も考えずに**を指定して、recursive=Trueと書けばいいっと。

Example.py

import glob

files = glob.glob('**', recursive=True)
print(files)

['123.txt', 'abc.txt', 'dir0', 'dir0\defg.txt', 'dir1', 'dir1\456.txt', 'dir1\dir2', 'dir1\dir2\7890.txt', 'Example.py', '[0].txt', '[].txt']

C
└── Root
   ├── Example.py
   ├── abc.txt
   ├── 123.txt
   ├── [].txt
   ├── [0].txt
   ├── dir0
   │  └── defg.txt
   └── dir1
      ├── 456.txt
      └── dir2
         └── 7890.txt

やりました、サブディレクトリ内のファイルやディレクトリも取得できました。

特殊文字を扱ってみよう

とりあえず glob を使う分に関しては上記の使い方で事足りそうです。
ただ意味も分からず * を引数で渡しているだけなので、もう少し調べておきましょう。

glob() の引数には検索したいファイルの文字列を指定するみたいですね。
そのため
glob.glob('123.txt')
みたいにピンポイントでファイル名を指定して探すことも出来るようですが、大抵の場合はもう少し大まかにファイルやディレクトリを検索したいときが多いはずです。
そんなときに特殊文字を引数に使うことで、 glob をもっと便利に使えるみたいです。

*

さっきから何も考えずに使ってる特殊文字ですね。
*長さ0文字以上の任意の文字列 を表すみたいですね。
ワイルドカード扱いの文字列なわけです。
* を引数で指定した場合は、
「名前はなんでもいいから、ファイルやディレクトリを探してくれぃ!」
という指定になるわけですね。

* と文字列を組み合わせることで、もう少し検索を絞り込めます。
以下の例では、
「.txt の拡張子のファイルを探してくれぃ!」となります。

Example.py

import glob

files = glob.glob('*.txt')
print(files)

['123.txt', 'abc.txt', '[0].txt', '[].txt']

C
└── Root
   ├── Example.py
   ├── abc.txt
   ├── 123.txt
   ├── [].txt
   ├── [0].txt
   ├── dir0
   │  └── defg.txt
   └── dir1
      ├── 456.txt
      └── dir2
         └── 7890.txt

サブディレクトリも含めて再帰的に検索したい場合は * を2つ指定する必要があります。
これも文字列を組み合わせることができるようです。
以下の例では、
「.txt の拡張子のファイルをサブディレクトリを含めて探してくれぃ!」となります。

Example.py

import glob

files = glob.glob('**/*.txt', recursive=True)
print(files)

['123.txt', 'abc.txt', '[0].txt', '[].txt', 'dir0\defg.txt', 'dir1\456.txt', 'dir1\dir2\7890.txt']

C
└── Root
   ├── Example.py
   ├── abc.txt
   ├── 123.txt
   ├── [].txt
   ├── [0].txt
   ├── dir0
   │  └── defg.txt
   └── dir1
      ├── 456.txt
      └── dir2
         └── 7890.txt

スラッシュの文字列を組み合わせて、特定のディレクトリ中にあるもの、みたいな指定だってできます。
以下の例では、
「何かしらのディレクトリの中にあるファイルやディレクトリを探してくれい!」となります。

Example.py

import glob

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

['dir0\', 'dir0\defg.txt', 'dir1\', 'dir1\456.txt', 'dir1\dir2', 'dir1\dir2\7890.txt']

C
└── Root
   ├── Example.py
   ├── abc.txt
   ├── 123.txt
   ├── [].txt
   ├── [0].txt
   ├── dir0
   │  └── defg.txt
   └── dir1
      ├── 456.txt
      └── dir2
          └── 7890.txt

?

? という特殊文字もあります。
?任意の1文字 を表すみたいですね。
?? なら2文字, ????? なら5文字を表す感じでしょうか。
以下の例では、
「ファイル名が3文字の拡張子が.txtのファイルを探してくれぃ!」となります。

Example.py

import glob

files = glob.glob('???.txt')
print(files)

['123.txt', 'abc.txt', '[0].txt']

C
└── Root
   ├── Example.py
   ├── abc.txt
   ├── 123.txt
   ├── [].txt
   ├── [0].txt
   ├── dir0
   │  └── defg.txt
   └── dir1
      ├── 456.txt
      └── dir2
         └── 7890.txt

[]特定の1文字 を表すみたいですね。
でも特定って何さ? という話になります。
[] はそのまま使うのではなく、 [a-z][0-9] といった形で使うようです。
それぞれ意味は、
- [a-z] aからzまでの文字
- [0-9] 0からpまでの数字
を表すみたいです。
[b-f], [4-6] みたいな使い方も出来るようです。
以下の例では、
「頭文字にaからzまでの文字を使用しているファイルやディレクトリの探してくれぃ!」となります。

Example.py

import glob

files = glob.glob('[a-z]*')
print(files)

['abc.txt', 'dir0', 'dir1', 'Example.py']

C
└── Root
   ├── Example.py
   ├── abc.txt
   ├── 123.txt
   ├── [].txt
   ├── [0].txt
   ├── dir0
   │  └── defg.txt
   └── dir1
      ├── 456.txt
      └── dir2
         └── 7890.txt

以下の例では、
「0から9の数字3文字の.txtファイルをサブディレクトリも含めて探してくれい!」となります。

Example.py

import glob

files = glob.glob('**/[0-9][0-9][0-9].txt', recursive=True)
print(files)

['123.txt', 'dir1\456.txt']

C
└── Root
   ├── Example.py
   ├── abc.txt
   ├── 123.txt
   ├── [].txt
   ├── [0].txt
   ├── dir0
   │  └── defg.txt
   └── dir1
      ├── 456.txt
      └── dir2
         └── 7890.txt

特殊文字そのものを検索する場合は?

では特殊文字そのものを検索する場合はどうすればいいのでしょうか。
……とはいえファイル名には ?* は使えないので [] がファイル名に含まれているものを探すときですね。
その場合は [] で括ってあげればいいみたいです
すなわち [ を探す場合は [[] と記述するのですね。
見辛い……。
以下の例では、
「頭文字に[を使っているファイルやらディレクトリを探してくれぃ!」となります。

Example.py

import glob

files = glob.glob('[[]*')
print(files)

['[0].txt', '[].txt']

C
└── Root
   ├── Example.py
   ├── abc.txt
   ├── 123.txt
   ├── [].txt
   ├── [0].txt
   ├── dir0
   │  └── defg.txt
   └── dir1
      ├── 456.txt
      └── dir2
         └── 7890.txt

イテレーターを返すだけでいいなら iglob

さて、今までは検索結果をリストで受け取りたかったので、 glob() を使ってきました。
ですが、検索結果に対して何か処理するだけでいい、イテレーターを受け取るだけで充分、といったときには iglob() というものがあるみたいですよ。
こちらの方は返り値をリスト化せずにイテレーターとして返してくれるようです。
リストの数が10個100個ぐらいのデータ量ならほとんどインパクトはないと思いますが、これが10万100万のデータ量となったら結構メモリを食ってしまいそうです。
リストにする必要がない場合は、 iglob() を使って処理したほうがよさそうです。

Example.py

import glob

for file in glob.iglob('**', recursive=True):
    print(file)

123.txt
abc.txt
dir0
dir0\defg.txt
dir1
dir1\456.txt
dir1\dir2
dir1\dir2\7890.txt
Example.py
[0].txt
[].txt

公式ドキュメント

glob に関する公式ドキュメントはこちら。
docs.python.org

glob を使えばファイルの検索が簡単に出来て便利ですー。