Python, pathlibでファイル名・拡張子・親ディレクトリを取得

Pythonのpathlibモジュールを使ってパスからファイル名(basename)や拡張子、親ディレクトリ(フォルダ)などを取得する方法を説明します。 pathlibはPython3.4から追加されたモジュール。ファイルやディレクトリのパスをオブジェクトとして操作できます。標準ライブラリに含まれているので追加のインストールは不要(importは必要)。

pathlib --- オブジェクト指向のファイルシステムパス — Python 3.7.1rc1 ドキュメント

ここでは以下の内容について説明します。

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

拡張子はsuffilx属性で取得できます。ピリオド.付きの文字列となります。

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()と同様に、拡張子を変更したファイルを作成したりするのに便利です。

Last Updated: 6/26/2019, 10:34:03 PM