ファイルとディレクトリのパスの処理
実用的なファイルシステム操作の詳細については、ブログ記事をご覧ください:すべての開発者が知っておくべき 10 の必須ファイルシステム操作。
Python でパス操作を扱う主要なモジュールは 2 つあります。 1 つは os.path モジュール、もう 1 つは pathlib モジュールです。
Pathlib と OS モジュール
pathlibは、ファイル名の取得、ファイル拡張子の取得、ファイルの読み書き(手動で開くことなく)など、上記にリストされているものよりもはるかに多くの機能を提供します。さらに詳しく知りたい場合は、公式ドキュメントを参照してください。
Linux と Windows のパス
Windows では、パスはフォルダ名の区切り文字としてバックスラッシュ (\) を使用して記述されます。macOS、Linux、BSD などの Unix ベースのオペレーティングシステムでは、パスの区切り文字としてフォワードスラッシュ (/) が使用されます。コードが異なるプラットフォームで動作する必要がある場合、パスの結合は頭痛の種になることがあります。
幸いなことに、Python の pathlib モジュールはこれを処理する簡単な方法を提供します。
*nix での pathlib の使用:
# pathlib.Path: クロスプラットフォームのパス処理
from pathlib import Path
print(Path('usr').joinpath('bin').joinpath('spam')) # パスコンポーネントを結合
usr/bin/spam
pathlib は、/ 演算子を使用して joinpath のショートカットも提供します:
# Path 演算子 (/): パスを結合する便利な方法 (クロスプラットフォーム)
from pathlib import Path
print(Path('usr') / 'bin' / 'spam') # joinpath() の代わりに / 演算子を使用
usr/bin/spam
パス区切り文字が Windows と Unix ベースのオペレーティングシステムで異なることに注意してください。これが、パスを結合するために文字列を結合する代わりに pathlib を使用したい理由です。
ログインしてこのクイズに回答し、学習の進捗を追跡できます
Path('usr') + 'bin' + 'spam'Path('usr') / 'bin' / 'spam'Path('usr').join('bin').join('spam')Path('usr/bin/spam')同じディレクトリの下に異なるファイルパスを作成する必要がある場合、パスの結合は役立ちます。
*nix での pathlib の使用:
# Path.home(): ユーザーのホームディレクトリを取得し、ファイル名と結合
my_files = ['accounts.txt', 'details.csv', 'invite.docx']
home = Path.home() # ホームディレクトリのパスを取得
for filename in my_files:
print(home / filename) # ホームパスと各ファイル名を結合
/home/labex/project/accounts.txt
/home/labex/project/details.csv
/home/labex/project/invite.docx
ユーザーホームディレクトリの展開
os.path.expanduser() を使用して ~ をユーザーのホームディレクトリに展開します:
import os.path
# ~ をユーザーのホームディレクトリに展開
print(os.path.expanduser('~'))
/home/labex/project
# ~/Documents を完全なパスに展開
print(os.path.expanduser('~/Documents'))
/home/labex/project/Documents
# ~ を含むパスでも機能する
print(os.path.expanduser('~/myfile.txt'))
/home/labex/project/myfile.txt
現在の作業ディレクトリ
pathlib を使用して現在の作業ディレクトリを取得できます:
# Path.cwd(): 現在の作業ディレクトリを取得
from pathlib import Path
print(Path.cwd()) # Path オブジェクトとして現在の作業ディレクトリを返す
/home/labex/project
新しいフォルダの作成
*nix での pathlib の使用:
from pathlib import Path
cwd = Path.cwd()
(cwd / 'delicious' / 'walnut' / 'waffles').mkdir()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.6/pathlib.py", line 1226, in mkdir
self._accessor.mkdir(self, mode)
File "/usr/lib/python3.6/pathlib.py", line 387, in wrapped
return strfunc(str(pathobj), *args)
FileNotFoundError: [Errno 2] No such file or directory: '/home/labex/project/delicious/walnut/waffles'
おっと、厄介なエラーが発生しました!理由は、‘delicious’ ディレクトリが存在しないため、その下に ‘walnut’ や ‘waffles’ ディレクトリを作成できないからです。これを修正するには、次のようにします:
# mkdir(parents=True): 必要に応じてディレクトリとすべての親ディレクトリを作成
from pathlib import Path
cwd = Path.cwd()
(cwd / 'delicious' / 'walnut' / 'waffles').mkdir(parents=True) # ネストされたディレクトリを作成
これで全てうまくいきました :)
絶対パスと相対パス
ファイルパスを指定するには 2 つの方法があります。
- 絶対パス: 常にルートフォルダから始まるパス
- 相対パス: プログラムの現在の作業ディレクトリに対して相対的なパス
また、ドット (.) とドットドット (..) フォルダもあります。これらは実際のフォルダではなく、パスで使用できる特別な名前です。単一のピリオド(ドット)は「このディレクトリ」の短縮形です。2 つのピリオド(ドットドット)は「親フォルダ」を意味します。
絶対パスの処理
pathlib を使用してパスが絶対パスであるかどうかを確認するには:
from pathlib import Path
Path('/').is_absolute()
True
Path('..').is_absolute()
False
ログインしてこのクイズに回答し、学習の進捗を追跡できます
Path('/').is_absolute() は何を返しますか?TrueFalseNone'/'pathlib を使用して絶対パスを抽出できます:
from pathlib import Path
print(Path.cwd())
/home/labex/project
print(Path('..').resolve())
/home
相対パスの処理
pathlib を使用して、開始パスから別のパスへの相対パスを取得できます:
from pathlib import Path
print(Path('/etc/passwd').relative_to('/'))
etc/passwd
パスとファイルの有効性
ファイル/ディレクトリの存在確認
*nix での pathlib の使用:
from pathlib import Path
Path('.').exists()
True
Path('setup.py').exists()
True
Path('/etc').exists()
True
Path('nonexistentfile').exists()
False
パスがファイルであることの確認
*nix での pathlib の使用:
from pathlib import Path
Path('setup.py').is_file()
True
Path('/home').is_file()
False
Path('nonexistentfile').is_file()
False
ログインしてこのクイズに回答し、学習の進捗を追跡できます
Path('setup.py').is_file() は何を返しますか?'setup.py'FalseTrueNoneパスがディレクトリであることの確認
*nix での pathlib の使用:
from pathlib import Path
Path('/').is_dir()
True
Path('setup.py').is_dir()
False
Path('/spam').is_dir()
False
ファイルサイズをバイト単位で取得する
*nix での pathlib の使用:
from pathlib import Path
stat = Path('/bin/python3.6').stat()
print(stat) # stat にはファイルに関する他の情報も含まれています
os.stat_result(st_mode=33261, st_ino=141087, st_dev=2051, st_nlink=2, st_uid=0,
--snip--
st_gid=0, st_size=10024, st_atime=1517725562, st_mtime=1515119809, st_ctime=1517261276)
print(stat.st_size) # バイト単位のサイズ
10024
ディレクトリのリスト表示
*nix で pathlib を使用してディレクトリの内容をリスト表示:
from pathlib import Path
for f in Path('/usr/bin').iterdir():
print(f)
...
/usr/bin/tiff2rgba
/usr/bin/iconv
/usr/bin/ldd
/usr/bin/cache_restore
/usr/bin/udiskie
/usr/bin/unix2dos
/usr/bin/t1reencode
/usr/bin/epstopdf
/usr/bin/idle3
...
ディレクトリのファイルサイズ
警告
ディレクトリ自体にもサイズがあります!したがって、上記セクションで説明したメソッドを使用して、パスがファイルかディレクトリかをチェックすることをお勧めします。
*nix での pathlib の使用:
from pathlib import Path
total_size = 0
for sub_path in Path('/usr/bin').iterdir():
total_size += sub_path.stat().st_size
print(total_size)
1903178911
ファイルとフォルダのコピー
shutil モジュールは、ファイルのコピー、およびフォルダ全体のコピーのための関数を提供します。
import shutil
shutil.copy('/tmp/spam.txt', '/tmp/delicious')
/tmp/delicious/spam.txt
shutil.copy('/tmp/eggs.txt', '/tmp/delicious/eggs2.txt')
/tmp/delicious/eggs2.txt
ログインしてこのクイズに回答し、学習の進捗を追跡できます
shutil.copy()Path.copy()os.copy()shutil.copytree()shutil.copy() は単一のファイルをコピーしますが、shutil.copytree() はフォルダ全体と、それに含まれるすべてのフォルダとファイルをコピーします:
import shutil
shutil.copytree('/tmp/bacon', '/tmp/bacon_backup')
/tmp/bacon_backup
移動と名前の変更
import shutil
shutil.move('/tmp/bacon.txt', '/tmp/eggs')
/tmp/eggs/bacon.txt
宛先パスでファイル名を指定することもできます。次の例では、ソースファイルが移動され、名前が変更されます:
shutil.move('/tmp/bacon.txt', '/tmp/eggs/new_bacon.txt')
/tmp/eggs/new_bacon.txt
eggs フォルダが存在しない場合、move() は bacon.txt を eggs という名前のファイルに名前変更します:
shutil.move('/tmp/bacon.txt', '/tmp/eggs')
/tmp/eggs
ファイルとフォルダの削除
Path.unlink()を呼び出すと、パス上のファイルが削除されます。Path.rmdir()を呼び出すと、パス上のフォルダが削除されます。このフォルダはファイルやフォルダを含んでいてはいけません。shutil.rmtree(path)を呼び出すと、パス上のフォルダが削除され、含まれるすべてのファイルとフォルダも削除されます。
ログインしてこのクイズに回答し、学習の進捗を追跡できます
Path.rmdir()shutil.rmtree()Path.unlink()os.remove()ディレクトリツリーのウォーク
Path オブジェクトには、ファイルとディレクトリを再帰的に反復処理するための rglob() メソッドがあります。
from pathlib import Path
p = Path('/tmp/delicious')
for i in p.rglob('*'):
print(i)
/tmp/delicious/cats
/tmp/delicious/walnut
/tmp/delicious/spam.txt
/tmp/delicious/cats/catnames.txt
/tmp/delicious/cats/zophie.jpg
/tmp/delicious/walnut/waffles
/tmp/delicious/walnut/waffles/butter.txt