[解決!Python]split関数でファイルパスを分割するには解決!Python

os.pathモジュールのsplit関数はファイルパスを末尾の要素とそれ以外に分割する。その使い方と注意点を紹介する。

» 2024年03月19日 05時00分 公開
[かわさきしんじDeep Insider編集部]
「解決!Python」のインデックス

連載目次

from os.path import split

p = '/tmp/foo/bar.txt'
result = split(p)
print(result)  # ('/tmp/foo', 'bar.txt')

p = 'C:\\tmp\\foo\\bar.txt'
result = split(p)
print(result)
# 出力結果:
# macOS:('', 'C:\\tmp\\foo\\bar.txt')
# Windows:('C:\\tmp\\foo', 'bar.txt')

# Windows上で動作するPythonと同様なパス分割を行う
import ntpath

p = 'C:\\tmp\\foo\\bar.txt'
result = ntpath.split(p)
print(result)  # ('C:\\tmp\\foo', 'bar.txt')

# パスがセパレーターで終わっている場合
p = '/tmp/foo/'
result = split(p)
print(result)  # ('/tmp/foo', '')

p = 'C:\\tmp\\foo\\'
result = split(p)
print(result)
# 出力結果:
# macOS:('', 'C:\\tmp\\foo\\')
# Windows:('C:\\tmp\\foo', '')

result = ntpath.split(p)
print(result)  # ('C:\\tmp\\foo', '')

p = '/tmp/foo'  # fooはディレクトリかファイルか分からない
result = split(p)
print(result)  # ('/tmp', 'foo')

# os.path.split関数とos.path.isdir関数、os.path.isfile関数を組み合わせる
Path('sample').mkdir(exist_ok=True)
Path('sample/foo').mkdir(exist_ok=True)
Path('sample/bar').mkdir(exist_ok=True)
Path('sample/foo.txt').touch(exist_ok=True)
Path('sample/bar.txt').touch(exist_ok=True)

import os

d = 'sample'
for item in os.listdir(d):
    p = d + '/' + item
    head, tail = split(p)
    if os.path.isdir(p):
        print(f'{tail} is a directory')
    elif os.path.isfile(p):
        print(f'{tail} is a file')

# パスにセパレーターが含まれていない場合
p = 'foo.txt'
result = split(p)
print(result)  # ('', 'foo.txt')

# パスがファイルシステムのルートを参照する場合
p = '/'
result = split(p)
print(result)

# Pathクラスのインスタンスをos.path.split関数に渡す
from pathlib import Path

p = Path('/tmp/foo/bar.txt')
result = split(p)
print(result)  # ('/tmp/foo', 'bar.txt')

p = Path('C:\\tmp\\foo\\bar.txt')
result = split(p)
print(result)  # UNIXとWindowsで結果が異なる

result = ntpath.split(p)
print(result)  # ('C:\\tmp\\foo', 'bar.txt')


os.path.split関数

 os.pathモジュールにはパスを特定の条件で分割する関数が幾つかある。

  • os.path.split関数:パスを「(パスの末尾より前, パスの末尾)」という2要素のタプルに分割する
  • os.path.splitdrive関数:パスを「(ドライブ, それ以外)」という2要素のタプルに分割する
  • os.path.splitext関数:パスを「(拡張子以外の部分, 拡張子)」という2要素のタプルに分割する
  • os.path.splitroot関数:パスを「(ドライブ, パスの末尾より前, パスの末尾)」という3要素のタプルに分割する(Python 3.12以降)

 このうち、以下ではos.path.split関数を使って、パスを末尾とそれ以外の部分に分割する方法を紹介する。

 以下にos.path.split関数の構文を示す。

os.path.splitext(path)


 os.path.split関数はパラメーターを1つ持ち、これにパスを渡すと「(パスの末尾以外の要素, 末尾要素)」という2つの要素からなるタプルが返される。ここでいう末尾以外の要素と末尾要素とは次のようなものだ。

  • 末尾以外の要素:ディレクトリを区切る最後のセパレーターより前の要素
  • 末尾要素:ディレクトリを区切る最後のセパレーターより後ろの要素

 セパレーターはUNIX系統のOSであればスラッシュ「/」であり、Windowsでは一般的にはバックスラッシュ「\」となる。

 基本的な例を以下に示す。

from os.path import split

p = '/tmp/foo/bar.txt'
result = split(p)
print(result)  # ('/tmp/foo', 'bar.txt')


 ここではos.path.split関数に'/tmp/foo/bar.txt'という文字列を渡している。末尾以外の要素は最後のセパレーターよりも前なので「/tmp/foo」となり、末尾要素は最後のセパレーターより後ろなので「bar.txt」となっているのが分かる。ただし、Windows形式のパスについては、実行しているOSによって結果が異なるので注意が必要だ。以下に例を示す。

p = 'C:\\tmp\\foo\\bar.txt'
result = split(p)
print(result)
# 出力結果:
# macOS:('', 'C:\\tmp\\foo\\bar.txt')
# Windows:('C:\\tmp\\foo', 'bar.txt')


 この例では'C:\\tmp\\foo\\bar.txt'という文字列をos.path.split関数に渡しているが、筆者がmacOSとWindowsで試したところでは、その戻り値はmacOSでは「('', 'C:\\tmp\\foo\\bar.txt')」に、Windowsでは「('C:\\tmp\\foo', 'bar.txt')」となった。

 これはUNIX系統のOSではos.pathモジュールの実体はposixpathモジュールであり、Windowsではos.pathモジュールの実体がntpathモジュールとなっていて、それぞれのOS(のファイルシステム)で都合が良くなるように処理が行われているからだ(os.pathモジュールはPythonプログラムを実行しているOSによって、ローカルのファイルパスを処理しやすいように切り替えられているということだ)。

 UNIX系統のOSでWindows上で動作しているPythonと同様にパスを分割したいのであれば、ntpathモジュールをインポートして、そのsplit関数を呼び出す。以下はその例だ。

import ntpath

p = 'C:\\tmp\\foo\\bar.txt'
result = ntpath.split(p)
print(result)  # ('C:\\tmp\\foo', 'bar.txt')


 逆に、Windows上でUNIX系統のOSで動作しているPythonと同様にパスを分割したいのであれば、posixpathモジュールをインポートして、そのsplit関数を呼び出せばよい(例は省略)。

特殊なケース

 パスがディレクトリを区切るセパレーターで終わっている場合、戻り値であるタプルの第1要素(2つ目の要素)は空文字列になる。UNIX形式のパス、Windows形式のパス、ntpathモジュールのsplit関数のそれぞれで試した例を以下に示す。

p = '/tmp/foo/'
result = split(p)
print(result)  # ('/tmp/foo', '')

p = 'C:\\tmp\\foo\\'
result = split(p)
print(result)
# 出力結果:
# macOS:('', 'C:\\tmp\\foo\\')
# Windows:('C:\\tmp\\foo', '')

result = ntpath.split(p)
print(result)  # ('C:\\tmp\\foo', '')


 パスがセパレーターで終わっていなければ、パスの末尾がディレクトリかファイルかに関係なく、末尾要素とそれ以外に分割される。

p = '/tmp/foo'  # fooはディレクトリかファイルか分からない
result = split(p)
print(result)  # ('/tmp', 'foo')


 パスが存在するかどうかはos.path.exists関数で、ディレクトリかどうかはos.path.isdir関数で、ファイルかどうかはos.path.isfile関数で調べられるので、処理を切り分けるのであれば、そうした関数の使用を考えよう(あるいはos.scandir関数で反復されるDirEntryクラスのis_dir/is_fileメソッドや、pathlib.Pathクラスのis_dir/is_fileメソッドでもよいだろう)。

 以下に例を示す(わざとらしい例だがご容赦願いたい)。

Path('sample').mkdir(exist_ok=True)
Path('sample/foo').mkdir(exist_ok=True)
Path('sample/bar').mkdir(exist_ok=True)
Path('sample/foo.txt').touch(exist_ok=True)
Path('sample/bar.txt').touch(exist_ok=True)

import os

d = 'sample'
for item in os.listdir(d):
    p = d + '/' + item
    head, tail = split(p)
    if os.path.isdir(p):
        print(f'{tail} is a directory')
    elif os.path.isfile(p):
        print(f'{tail} is a file')


 この例ではカレントディレクトリにsampleディレクトリを作成して、その下にさらにディレクトリとファイルを幾つか作成している。その後、sampleディレクトリを起点にファイル/ディレクトリを反復し、それがディレクトリかファイルかで処理を切り分けている。上のコードでos.listdir関数はsampleディレクトリからの相対パスを反復するので、os.path.isdir関数やos.path.isfile関数でパスがディレクトリかファイルかを判定できるようにパスを加工している。加工後のパスは、os.path.split関数で分割して、sampleディレクトリにあるファイルやディレクトリの名前を取り出している(ここがわざとらしいところで、実際には「f'{item} is ……'」でよい)。

 また、パスにセパレーターが含まれていない場合(つまり、カレントディレクトリにあるファイルやディレクトリをパスが参照している場合)には、戻り値であるタプルの第0要素は常に空文字列となる。

# パスにセパレーターが含まれていない場合
p = 'foo.txt'
result = split(p)
print(result)  # ('', 'foo.txt')


 os.path.split関数が返すタプルの第0要素は最後にセパレーターが付かないが、パスとしてルートを渡したときだけはセパレーターで終わる。

p = '/'
result = split(p)
print(result)


os.path.split関数とpath-like object

 os.path.split関数は文字列以外にもpath-like objectを受け取れる。そうしたオブジェクトの典型例がpathlibモジュールのPathクラスだ。以下にos.path.split関数にPathクラスのインスタンスを渡す例を示す。

from pathlib import Path

p = Path('/tmp/foo/bar.txt')
result = split(p)
print(result)  # ('/tmp/foo', 'bar.txt')


 ここではUNIX形式のファイルパスを渡しているので、その結果はUNIX形式のファイルパスを文字列として渡したときと変わらない。なお、戻り値であるタプルの要素はPathクラスのインスタンスではなく、文字列であることには注意しよう。

 Windows形式のファイルパスでもこれは同様だ(つまり、WindowsとUNIX系統のOSで分割結果が異なる)。

p = Path('C:\\tmp\\foo\\bar.txt')
result = split(p)
print(result)  # UNIXとWindowsで結果が異なる

result = ntpath.split(p)
print(result)  # ('C:\\tmp\\foo', 'bar.txt')


 なお、Windows環境以外でWindows形式のパスからPathクラスのインスタンスを生成しようすると、実際にはpathlib.PosixPathクラスのインスタンスが生成されることには注意しよう(これに対して、Windows環境でPathクラスのインスタンスを生成しようとすると、実際にはpathlib.WindowsPathクラスのインスタンスが生成される)。pathlibモジュールにはWindows形式のファイルパスを表すクラスとしてPureWindowsPathクラスがある。これはWindowsのファイルシステムとは関係なく、Windows形式のパスの操作を確認するために便利に使える。

from pathlib import PureWindowsPath

p = PureWindowsPath('C:\\tmp\\foo\\bar.txt')
result = split(p)
print(result)  # ('', 'C:\\tmp\\foo\\bar.txt')

result = ntpath.split(p)
print(result)  # ('C:\\tmp\\foo', 'bar.txt')


 上の例を見ると分かるが、PureWindowsPathクラスはWindows形式のパスを表すものの、Windows環境でのパス分割と同じ結果を得たいのであれば、加えてntpathモジュールを使用する必要がある。

「解決!Python」のインデックス

解決!Python

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。