【Python】最近使ったファイル/ディレクトリを操作するモジュールまとめ

こんにちは。AI-Tech-Lab愛知支部で学生エンジニアをしている松田です。

今回は、Pythonのライブラリを用いてファイルやディレクトリ(フォルダ)を操作する方法をまとめていこうと思います。記事の前半がモジュール別の関数まとめ、後半が実際の使用例となっています。

経緯としましては、最近、機械学習用に動かすプログラムの指定に対応したディレクトリ構造でファイルをまとめたり、そのファイルに必要なデータを書き込んだりする作業をすることが多かった為、その際によく使用したライブラリを紹介しようと思った次第です。

僕の備忘録兼、同じような作業をされる方の手助けになれれば幸いです。

モジュール別の関数まとめ

osモジュール

ファイルやディレクトリの「作成」「削除」「一覧取得」などに使用します。

import osでインポート出来ます。主な関数は以下の通りです。

Python
#ディレクトリの作成
os.mkdir("ディレクトリ名")

#ディレクトリの削除(中身が空でないと削除不可)
os.rmdir("ディレクトリ名")

#ファイルの削除
os.remove("ファイル名")

#ファイル/ディレクトリの一覧をリストで取得
filelist = os.listdir("ディレクトリ名")

globモジュール

「特定の文字を含むファイルへのパスを取得」または「ディレクトリ下の各ファイルへのパスを取得」などに使用します。

import globでインポート出来ます。主な関数は以下の通りです。

Python
#拡張子が".png"のファイルへのパスをリストで取得
pathlist = glob.glob("*.png")

#ディレクトリ下の各ファイルへのパスをリストで取得
pathlist = glob.glob("ディレクトリ名" + "/*")

ちなみに、「*」は何でもよいという意味です。例えば「〇〇.png」のファイルが欲しい時は〇〇の部分は何でもよいので、「*.png」としているわけです。従って、何も指定しない場合、すなわちディレクトリ下のファイルへのパスを全部取得したい場合は「*」とだけ書けばよいということになります。

shutilモジュール

ファイルやディレクトリの「コピー」「移動」「削除」などに使用します。

import shutilでインポート出来ます。主な関数は以下の通りです。

Python
#ファイルを指定したディレクトリ下へコピー
shutil.copy("ファイル名(コピー元)", "ディレクトリ名(コピー先)")

#ディレクトリを丸ごと指定したディレクトリ下へコピー
shutil.copytree("ディレクトリ名(コピー元)", "ディレクトリ名(コピー先)")

#ディレクトリを丸ごと指定したディレクトリ下へ移動
shutil.move("ディレクトリ名(コピー元)", "ディレクトリ名(コピー先)")

#ディレクトリ下を丸ごと削除
shutil.rmtree("ディレクトリ名")

その他ファイルからデータを取り出す際に便利な関数

split関数:例えばファイル名を指定の記号で分割する時に使用。以下使用例。

Python
filename = "2022_11_11_picture1.png"

#ピリオドで分割する
namelist = filename.split(".")
print(namelist[0])
print(namelist[1])

>>2022_11_11_picture1
>>png

open関数:ファイルを開く時に使用。以下使用例。

Python
#オプションを指定してファイルを開く。読み込みなら'r' 上書きなら'w' 追記なら'a'
f = open("ファイル名", 'オプション')

#ファイルへの操作が終わったら閉じる。
f.close()

readlines関数:テキストファイルを行ごとにリストで読み込む時に使用。以下使用例。

HTML
f = open("ファイル名", 'オプション')

#テキストファイルを行ごとにリストで取得
datalist = f.leadlines()

#1行目
line_1 = datalist[0]
#2行目
line_2 = datalist[1]

f.close()

os / shutil / globモジュールの使用する場面

例として、以下のような「sample」ディレクトリを使って説明していきます。

「copy_files.ipynb」は、「copy_from」内のファイルをpng, json, その他の拡張子別にそれぞれ「copy_to」内の「directory_A」「directory_B」「directory_C」にコピーするプログラムとします。(jupyter notebookを使用した為プログラムの拡張子はipynbになっています)

まず、「copy_form」のディレクトリ内から、指定の拡張子のファイルを分類する必要があります。ここでは、globモジュールが便利です。また、リスト同士の要素の引き算を行いたい時は、set型にキャストすることで引き算を行うことが出来ます。

python
import glob
png_pathlist = glob.glob("copy_from/*png")
json_pathlist = glob.glob("copy_from/*json")
other_pathlist = set(glob.glob("copy_from/*")) - set(png_pathlist) - set(json_pathlist)
other_pathlist = list(other_pathlist)

これで拡張子別のリストが出来たので、後はfor文の中でshutil.copyの関数を実行すればファイルを指定のディレクトリへコピーすることが出来ます。

Python
import shutil

#pngファイルをdirectory_Aへコピー
for path in png_pathlist:
  shutil.copy(path, "./copy_to/directory_A")

#jsonファイルをdirectory_Bへコピー
for path in json_pathlist:
  shutil.copy(path, "./copy_to/directory_B")

#その他のファイルをdirectory_Cへコピー
for path in other_pathlist:
  shutil.copy(path, "./copy_to/directory_C")

globの利点として、ファイルへの”パス”としてリストが作成されることも挙げられます。今回使用したshutilモジュール以外でも、ファイルを触る関数のほとんどはファイルへのパスを引数とするので、対象のファイルがどこにあるかいちいち詳細に記述する必要が無い分楽が出来ます。

逆に、その辺りを見やすくしたい場合はos.listdirの関数でディレクトリ下のファイルのリスト取得し、パスを指定する際に「”ディレクトリ名” + “/” + “ファイル名”」のようにする方法もありです。png, jsonの文字列がファイル名に含まれているかどうかは、if文で判定して振り分けることが出来ます。

Python
import os
PATH_COPY_FROM = "./copy_from"
file_list = os.listdir(PATH_COPY_FROM)
for file in file_list:
  #fileに"png"の文字列が含まれるか否かの判定
  if('png' in file):
    shutil.copy(PATH_COPY_FROM + "/" + file, "./copy_to/directory_A")
  elif('json' in file):
    shutil.copy(PATH_COPY_FROM + "/" + file, "./copy_to/directory_B")
  else:
    shutil.copy(PATH_COPY_FROM + "/" + file, "./copy_to/directory_C")

さて、これで「copy_files.ipynb」は完成ですが、このコードを使用して何回か作業を行う場合を考えてみましょう。

作業をやり直すならば、A,B,Cで分かれているディレクトリの中身をそれぞれ削除するよりも、「copy_to」のディレクトリごと削除出来た方が楽です。しかし、この場合「copy_to」のディレクトリから、その中にある「directory_A」「directory_B」「directory_C」を再び作り直さなければなりません。

完成した「copy_to」のディレクトリはそのまま保存しておき、別に新たな「copy_to」のディレクトリを作成したい場合もあるでしょう。このような場合も、やはり必要なディレクトリを作り直す必要がありそうです。

何が言いたいかと言うと、つまり、全てのディレクトリを一括作成してくれるプログラムもセットで作っておくと後々便利であるということです。(下図では「make_directory.ipynb」としている)

このとき、os.mkdirの関数が役に立ちます。

Python
import os
dir_list = ["directory_A", "directory_B", "directory_C"]

#上層ディレクトリの作成
os.mkdir("copy_from")
os.mkdir("copy_to")

#copy_to以下のディレクトリはforループで作成
for list in dir_list:
    os.mkdir("copy_to" + "/" + list)

これで、ディレクトリを削除したり、新規の作業に取り掛かる際にも、必要なディレクトリをすぐに用意出来ます。

自分以外の人にプログラムを共有する際にも、こちらの方が親切です。

余談ですが、zip圧縮をする場合にも、この方法が有効な場面があります。一度、全てのディレクトリが空の状態で、sampleディレクトリの中身をzip圧縮しようとすれば理由が分かります。(中身でなくsample自体をzip圧縮すると、解凍時に無駄な階層が出来てしまうので必ず中身を全選択してzip圧縮するようにします。)

このように、空のディレクトリ、すなわち今回だと「copy_from」のディレクトリはzipファイルから弾かれてしまいます。こうなるくらいならば、始めから「copy_from」や「copy_to」のディレクトリは作っておかないで、代わりに「make_directory.ipynb」のようなプログラムを同封しておくのが良いでしょう。

まとめ

今回はPythonのライブラリを用いてファイルやディレクトリ(フォルダ)を操作する方法から、その使用例を紹介しました。

プログラムの作成時、データ整理は下準備の段階になるので、この工程がストレスなく出来るようになると、本題に割く時間を増やす事が出来ますね。