Python, pathlibでファイル名・拡張子・親ディレクトリを取得
Pythonのpathlibモジュールを使ってパスからファイル名(basename)や拡張子、親ディレクトリ(フォルダ)などを取得する方法を説明する。
pathlibはPython3.4から追加されたモジュール。ファイルやディレクトリのパスをオブジェクトとして操作できる。標準ライブラリに含まれているので追加のインストールは不要(import
は必要)。
ここでは以下の内容について説明する。
Path
オブジェクトの基礎- ファイル名(basename)、ディレクトリ名を取得:
name
,stem
- 拡張子を取得:
suffix
- 親ディレクトリを取得:
parent
,parents[]
- 同じディレクトリの別のファイルを取得:
with_name()
- 拡張子を変更したパスを取得:
with_suffix()
パスの文字列を使ってファイル名などを取得する従来のos.path
については以下の記事を参照。
慣れると文字列ベースのos.path
よりもオブジェクトベースのpathlibモジュールのほうが使いやすい。
pathlibの基本的な使い方については以下の記事を参照。
以下のようなファイル・ディレクトリ(フォルダ)構成を例とする。
temp
├── dir
│ └── sub_dir
│ └── file2.txt
└── file.txt
Pathオブジェクトの基礎
pathlib.Path()
でPath
オブジェクトを生成して操作する。引数に相対パスまたは絶対パスでパスを指定する。
import pathlib
p_file = pathlib.Path('temp/file.txt')
print(p_file)
# temp/file.txt
print(type(p_file))
# <class 'pathlib.PosixPath'>
OSによってPosixPath
またはWindowsPath
のインスタンスとなる。
文字列に変換したい場合はstr()
を使う。
print(str(p_file))
# temp/file.txt
print(type(str(p_file)))
# <class 'str'>
詳しくは以下の記事を参照。
ファイル名(basename)、ディレクトリ名を取得: name, stem
パスの末尾のファイル名(basename)の文字列を取得するにはname
属性を使う。
print(p_file.name)
# file.txt
print(type(p_file.name))
# <class 'str'>
拡張子なしのファイル名の文字列はstem
属性で取得可能。
print(p_file.stem)
# file
print(type(p_file.stem))
# <class 'str'>
ディレクトリを指すPath
オブジェクトの場合は、name
もstem
どちらも末尾(最下層)のディレクトリ名の文字列を返す。
p_dir = pathlib.Path('temp/dir/')
print(p_dir)
# temp/dir
print(type(p_dir))
# <class 'pathlib.PosixPath'>
print(p_dir.name)
# dir
print(p_dir.stem)
# dir
拡張子を取得: suffix
拡張子はsuffix
属性で取得できる。ピリオド.
付きの文字列となる。
print(p_file.suffix)
# .txt
print(type(p_file.suffix))
# <class 'str'>
ディレクトリの場合は空文字列。
print(p_dir.suffix)
#
ピリオドが必要ない場合はlstrip()
で先頭のピリオドを削除するか、スライスで2文字目以降を取得する。
print(p_file.suffix.lstrip('.'))
# txt
print(p_file.suffix[1:])
# txt
ディレクトリや拡張子がないファイル名の場合はどちらの方法でも空文字列のまま。
print(p_dir.suffix.lstrip('.'))
#
print(p_dir.suffix[1:])
#
親ディレクトリを取得: parent, parents
サブディレクトリにあるファイルを示すPath
オブジェクトを例とする。
p_sub = pathlib.Path('temp/dir/sub_dir/file2.txt')
print(p_sub)
# temp/dir/sub_dir/file2.txt
parent
属性で親ディレクトリのPath
オブジェクトにアクセスできる。os.path.dirname()
に相当するが、parent
が返すのは文字列ではなくPath
オブジェクト。
print(p_sub.parent)
# temp/dir/sub_dir
print(type(p_sub.parent))
# <class 'pathlib.PosixPath'>
上位階層に一気にアクセスしたい場合はparent.parent
のようにparent
を繰り返してもいいが、parents[]
を使うと便利。parents[0]
が一階層上(親ディレクトリ)、parents[1]
が二階層上、parents[2]
が三階層上…となる。
print(p_sub.parents[0])
# temp/dir/sub_dir
print(p_sub.parents[1])
# temp/dir
print(p_sub.parents[2])
# temp
print(p_sub.parents[3])
# .
例のように相対パスで指定した場合、カレントディレクトリより上位階層にはいけない。
# print(p_sub.parents[4])
# IndexError: 4
さらに上の階層にアクセスしたい場合はresolve()
で絶対パスに変換する。
p_abs = p_sub.resolve()
print(p_abs)
# /Users/mbp/Documents/my-project/python-snippets/notebook/temp/dir/sub_dir/file2.txt
print(p_abs.parents[4])
# /Users/mbp/Documents/my-project/python-snippets
当然ながら、この場合もルートより上の位置を指定するとエラーとなる。
# print(p_abs.parents[10])
# IndexError: 10
..を含む場合の注意
相対パスで一階層上を表す..
を使っている場合は注意が必要。
同じファイルを指す以下の2つのPath
オブジェクトを例とする。
p_file = pathlib.Path('temp/file.txt')
print(p_file)
# temp/file.txt
p_file_rel = pathlib.Path('temp/dir/sub_dir/../../file.txt')
print(p_file_rel)
# temp/dir/sub_dir/../../file.txt
print(p_file.samefile(p_file_rel))
# True
この2つのPath
オブジェクトのparents[]
は以下のようになる。
print(p_file.parents[0])
# temp
print(p_file.parents[1])
# .
print(p_file_rel.parents[0])
# temp/dir/sub_dir/../..
print(p_file_rel.parents[1])
# temp/dir/sub_dir/..
print(p_file_rel.parents[2])
# temp/dir/sub_dir
print(p_file_rel.parents[3])
# temp/dir
parent
およびparents[]
は純粋な字句操作であるため、元が同じファイルやディレクトリを示していても、..
が含まれている場合と含まれていない場合で結果が異なる。
resolve()
で絶対パスに変換すると..
は除去されるので、..
を含んでいる場合は、まずresolve()
で絶対パスに変換してからparent
やparents[]
を使ったほうが間違いを防げる。
print(p_file_rel.resolve())
# /Users/mbp/Documents/my-project/python-snippets/notebook/temp/file.txt
..
を除去した上でカレントディレクトリからの相対パスに変換するには、resolve()
とrelative_to()
, cwd()
を使う。
print(p_file_rel.resolve().relative_to(p_file_rel.cwd()))
# temp/file.txt
pathlibにおける絶対パス・相対パスの処理についての詳細は以下の記事を参照。
同じディレクトリの別のファイルを取得: with_name()
ファイルを指すPath
オブジェクトを例とする。
p_file = pathlib.Path('temp/file.txt')
with_name()
を使うと、現在のパスのname
属性を変更したPath
オブジェクトが返される。引数に新たなname
属性を文字列で指定する。
ファイルを指すPath
オブジェクトに対してwith_name()
を使いファイル名を引数に指定すると、同じディレクトリ(同じ階層)の別のファイルを指すPath
オブジェクトが取得できる。
print(p_file.with_name('file_new.txt'))
# temp/file_new.txt
print(type(p_file.with_name('file_new.txt')))
# <class 'pathlib.PosixPath'>
ディレクトリを示すPath
オブジェクトの場合も同様。
p_dir = pathlib.Path('temp/dir/')
with_name()
はあくまでも現在のパスのname
属性の変更。Path
オブジェクト自体はファイルとディレクトリの区別はないので、ディレクトリを示すPath
オブジェクトからwith_name()
で新たなファイル名を指定することもできる。
print(p_dir.with_name('dir_new'))
# temp/dir_new
print(p_dir.with_name('file_new.txt'))
# temp/file_new.txt
with_name()
は同じディレクトリに新たなファイルやディレクトリを作成するのに便利。
新たなファイル名やディレクトリ名を指定してまだ存在しないパスに対するPath
オブジェクトを取得し、そのPath
オブジェクトを使って新規ファイルや新規ディレクトリを作成できる。
空のファイルを作成するtouch()
メソッドを使った例は以下の通り。
p_file.with_name('file_new.txt').touch()
print(p_file.with_name('file_new.txt').exists())
# True
pathlibを使ったファイルやディレクトリの作成は以下の記事を参照。
拡張子を変更したパスを取得: with_suffix()
with_name()
に似たメソッドにwith_suffix()
がある。
with_suffix()
は元のパスのsuffix
属性(拡張子)を変更したPath
オブジェクトを返す。引数に新たな拡張子を文字列で指定する。
print(p_file.with_suffix('.text'))
# temp/file.text
print(type(p_file.with_suffix('.text')))
# <class 'pathlib.PosixPath'>
先頭のピリオド.
がないとエラーとなるので注意。
# print(p_file.with_suffix('text'))
# ValueError: Invalid suffix 'text'
with_suffix()
もwith_name()
と同様に、拡張子を変更したファイルを作成したりするのに便利。