Pythonでdosパスをコンポーネントに分割する方法


153

dosパスを表す文字列変数があります。例:

var = "d:\stuff\morestuff\furtherdown\THEFILE.txt"

この文字列を次のように分割します。

[ "d", "stuff", "morestuff", "furtherdown", "THEFILE.txt" ]

私が使って試してみましたsplit()し、replace()彼らはどちらかのみ最初のバックスラッシュを処理するか、彼らは文字列に16進数を挿入します。

この文字列変数を何らかの方法で生の文字列に変換して、解析できるようにする必要があります。

これを行う最良の方法は何ですか?

また、varつまり、解析しようとしているパスの内容が、実際にはコマンドラインクエリの戻り値であることも追加する必要があります。私が生成するのはパスデータではありません。ファイルに保存され、コマンドラインツールはバックスラッシュをエスケープしません。


6
これらの回答を確認os.path.splitするとき、その文字列を適切にエスケープしていないため、は機能しないことに注意してください。
ジェド・スミス

文字列をエスケープするか、rawstring:r"d:\stuff\morestuff\furtherdown\THEFILE.txt"を使用し\sて、誤解などを防ぐ必要があります。
smci

回答:


164

自分でパスをいじって関数を書き、それを誤解する人々に何度も噛まれました。スペース、スラッシュ、バックスラッシュ、コロン-混乱の可能性は無限ではありませんが、とにかく間違いは簡単に起こります。だから私はを使うのが好きで、os.pathそれに基づいてそれをお勧めします。

(しかし、美徳への道は最も簡単に取られる道ではありません、そしてこれを見つけた多くの人々はつまずきへの滑りやすい道をまっすぐに破滅へと誘惑する誘惑に駆られます。おそらく、他の誰かが-すべてがうまくいかなかった理由を解明する必要があり、誰かがスラッシュとバックスラッシュを混合したファイル名を作成したことが判明しました-そして誰かが答えは「それをしないこと」であると示唆しています。スラッシュとバックスラッシュを混同した人を除いて、これらの人のいずれかである必要があります。

次のようにドライブとパス+ファイルを取得できます。

drive, path_and_file = os.path.splitdrive(path)

パスとファイルを取得します。

path, file = os.path.split(path_and_file)

個々のフォルダ名を取得することは特に便利ではありませんが、後で実際にうまく機能するものを見つける喜びを高めるのは、一種の正直で中途半端な不快感です。

folders = []
while 1:
    path, folder = os.path.split(path)

    if folder != "":
        folders.append(folder)
    else:
        if path != "":
            folders.append(path)

        break

folders.reverse()

(これは、パスが元々絶対パスだった場合"\"、最初にa をポップしfoldersます。それを望まない場合、コードが少し失われる可能性があります。)


@brone-バックスラッシュをエスケープすることを心配する必要があるよりも、私はこのソリューションを使用することを好みます。ありがとう!
BeeBand

1
私は間違って証明されて嬉しいですが、この「C:\ usr \ rs0 \ my0 \ in111102.log」のようなパスが使用されている場合、解決策が機能しないことを示唆しているようです(初期入力が生の文字列でない限り) )?
シャーリチャード

1
"/ path / to / my / folder /"のようなOSXのディレクトリのみが含まれている場合、これはパスを適切に分割しないように見えます。if path.endswith("/"):path = path[:-1]
ケビンロンドン

1
私は@Tompaによる解決策を好む
jaycode

1
私は同意jaycodeトンパ解決策はある標準的なアプローチと受け入れ答えていたはずです。この過度に複雑で非効率的でエラーが発生しやすい代替手段は、本番用コードで必須条件を通過できません。ありません何の試み(...と、もちろん、失敗する)単純な文字列の分割のみで成功した場合、反復パス名を離れ解析するために合理的な理由コードの1行。
Cecil Curry、

287

私はするだろう

import os
path = os.path.normpath(path)
path.split(os.sep)

まず、パス文字列をOSに適した文字列に正規化します。次にos.sep、文字列関数分割の区切り文字として安全に使用できる必要があります。


25
正解は1つです。それが浮上しました。もちろん、標準的なソリューションが最も簡単です。見よ!それはエレガントで控えめなものであり、不十分なエッジケースはありません。
Cecil Curry

20
ワンライナーとしてos.path.normpath(a_path).split(os.path.sep)
Daniel Farrell

2
これは、path = rootでは機能しないようです。その場合、path.splitの結果は[''、 '']になります。実際、一般に、このsplit()ソリューションは、空の文字列名(適切なスラッシュで置き換えることができます)を持つ左端のディレクトリを提供します。中心的な問題は、単一のスラッシュ(OSに応じて前方または後方)がルートディレクトリの名前であり、パスの他の場所では区切り記号であることです。
gwideman

2
それでは、それはlstripでよりよく機能しますか?os.path.normpath(path).lstrip(os.path.sep).split(os.path.sep)
Vidar 2017

1
@ user60561これは、Linuxではファイル名にバックスラッシュを使用できるのに対し、Windowsではスラッシュを使用できないためです。そのため、Windowsではnormpathスラッシュを区切り文字として認識します。Linuxでは、normpathというディレクトリが\1\2あり、その中にファイルまたはディレクトリがあると仮定します3
Vojislav Stojkovic

81

あなたは単に最もPython的なアプローチ(IMHO)を使うことができます:

import os

your_path = r"d:\stuff\morestuff\furtherdown\THEFILE.txt"
path_list = your_path.split(os.sep)
print path_list

これはあなたに与えるでしょう:

['d:', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']

ここでの手掛かりはos.sep'\\'またはの代わりに使用'/'することです。これにより、システムに依存しなくなります。

ドライブ文字からコロンを削除するには(その理由はわかりませんが)、次のように書くことができます。

path_list[0] = path_list[0][0]

22
これは機能しsome timesます。その他の場合(少なくともWindowsでは)、次のようなパスが表示されますfolder\folder2\folder3/file.txt。最初にパスを正規化(os.path.normpath)してから、それを分割する方が良いでしょう。
vikki 14

7
この答えはほとんどありませんでした。VIKKIが示唆、文字列分割呪文の前に正規化パス名に障害が当たり前のエッジケースに運命(例えば、/foo//bar)。より堅牢なソリューションについては、Tompa回答を参照してください。
Cecil Curry

62

Python> = 3.4では、これはずっと簡単になりました。これで、を使用pathlib.Path.partsしてパスのすべての部分を取得できます。

例:

>>> from pathlib import Path
>>> Path('C:/path/to/file.txt').parts
('C:\\', 'path', 'to', 'file.txt')
>>> Path(r'C:\path\to\file.txt').parts
('C:\\', 'path', 'to', 'file.txt')

Python 3のWindowsインストールでは、これはWindowsパスで作業していると想定し、* nixではposixパスで作業していると想定します。通常はこれで十分ですが、そうでない場合は、クラスを使用するpathlib.PurePosixPathpathlib.PureWindowsPath、必要に応じて次のように使用できます。

>>> from pathlib import PurePosixPath, PureWindowsPath
>>> PurePosixPath('/path/to/file.txt').parts
('/', 'path', 'to', 'file.txt')
>>> PureWindowsPath(r'C:\path\to\file.txt').parts
('C:\\', 'path', 'to', 'file.txt')
>>> PureWindowsPath(r'\\host\share\path\to\file.txt').parts
('\\\\host\\share\\', 'path', 'to', 'file.txt')

編集:利用可能なPython 2へのバックポートもあります:pathlib2


3
Path.partsは常に欲しかったものですが、それが今日まで存在することを知りませんでした。
JamEnergy

なぜこれは素晴らしいネイティブPython関数をラップしていないのですか?
Eduardo Pignatelli、

2
それが答えです!
nayriz

11

ここでの問題は、最初に文字列を作成する方法から始まります。

a = "d:\stuff\morestuff\furtherdown\THEFILE.txt"

:この方法を行って、Pythonは特殊なケースこれらにしようとしている\s\m\f、と\T。あなたの場合、\f他のバックスラッシュが正しく処理されている間、はフォームフィード(0x0C)として扱われます。あなたがする必要があるのはこれらの1つです:

b = "d:\\stuff\\morestuff\\furtherdown\\THEFILE.txt"      # doubled backslashes
c = r"d:\stuff\morestuff\furtherdown\THEFILE.txt"         # raw string, no doubling necessary

次に、これらのいずれかを分割すると、必要な結果が得られます。


@W。Craig Trader-ありがとう、でもこのパスは私が自分で生成したものではありません。別のプログラムから戻ってきて、このデータを変数に格納する必要があります。変数に格納されているデータを「生のテキスト」に変換する方法がわかりません。
BeeBand

「生のテキスト」のようなものはありません...それはあなたがソースでそれを表現する方法です。文字列の前にr ""を追加するか、.replace( '\\'、 '/')を介して渡します
Marco Mariani

@BeeBand、他のプログラムからデータをどのように取得していますか?ファイル、パイプ、ソケットから読み取っていますか?その場合は、特別なことをする必要はありません。バックスラッシュを2倍にする、または生の文字列を使用する唯一の理由は、Pythonコードに文字列定数を配置することです。一方、他のプログラムが二重のバックスラッシュを生成している場合は、パスを分割する前にそれをクリーンアップする必要があります。
Craig Trader

@W。Craig Trader-別のプログラムによって書き込まれたファイルから読み取ります。何らかの理由で取得できなかっsplit()たりreplace()、機能しなかった-16進数の値を取得し続けました。あなたは正しいですが、私は生の文字列のアイデアで間違ったツリーを吠えていたと思います-私はsplit()間違って使用していたと思います。私が使用split()してこれらのソリューションのいくつかを試したので、それらは今私のために働きます。
BeeBand 2010

10

やや簡潔な解決策として、以下を検討してください。

def split_path(p):
    a,b = os.path.split(p)
    return (split_path(a) if len(a) and len(b) else []) + [b]

これは、この問題に対する私のお気に入りの解決策です。非常に素晴らしい。
Will Moore

1
パスがで終わる場合、これは機能しません/。また、パスが/
Sorig

4

私は実際にこれに実際の答えを提供することはできません(私が自分で見つけようとしてここに来たので)、私にはさまざまなアプローチの数と言及されたすべての警告が、Pythonのos.pathモ​​ジュールが必死にこれを必要とする最も確実な指標です組み込み関数として。


4

ジェネレータを使用した機能的な方法。

def split(path):
    (drive, head) = os.path.splitdrive(path)
    while (head != os.sep):
        (head, tail) = os.path.split(head)
        yield tail

実行中:

>>> print([x for x in split(os.path.normpath('/path/to/filename'))])
['filename', 'to', 'path']

3

わたしにはできる:

>>> a=r"d:\stuff\morestuff\furtherdown\THEFILE.txt"
>>> a.split("\\")
['d:', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']

もちろん、最初のコンポーネントからコロンも取り除く必要があるかもしれませんが、それを維持することでパスを再構成することができます。

r「生」などの修飾マークの文字列リテラル。埋め込まれたバックスラッシュが2倍にならないことに注意してください。


@unwind- r文字列の前の、それは何を指しますか?
BeeBand

2
rは生の文字列を意味します-文字を自動エスケープし\ ます。パスを実行しているときはいつでも使用すると便利です。
Wayne Werner

1
@BeeBand:気にする必要はありません。r ""は、コードのコンパイル/解析中に重要なものであり、一度解析された文字列のプロパティになるものではありません。これは単に「文字列リテラルですが、バックスラッシュをバックスラッシュ以外の意味を持つものとして解釈しないでください」という意味です。
2010

3
ハードコーディングするのではなく、a.split(os.sep)を使用することで、あいまいになる可能性があることをお伝えしておきましょう。
Tim McJilton、2010

4
説明する機会を逃してしまったためos.path.split、私はあなたに反対票を投じなければなりos.pathsepません。今はOPには関係ないかもしれませんが、プラットフォームを移動する必要がある何かを書いているときは問題になります。
ジェド・スミス

3

約のものmypath.split("\\")は、としてよりよく表現されmypath.split(os.sep)ます。sep特定のプラットフォーム(\Windows、/Unixなど)のパス区切り文字で、Pythonビルドはどちらを使用するかを認識しています。を使用するとsep、コードはプラットフォームに依存しなくなります。


1
またはos.path.split。OS Xの私のバージョンのPythonにある(そしてを適切に処理する)os.pathsepため、注意が必要です。:os.path.split/
ジェドスミス2010

4
そうos.sepじゃないos.pathsepos.sepドキュメントの知識に従ってください:パス名を解析または連結するには、これを理解するだけでは不十分です。os.path.split()およびos.path.join()を使用してください。
Jon-Eric

1

re.split()は、string.split()よりも少しだけ役立ちます

import re    
var = "d:\stuff\morestuff\furtherdown\THEFILE.txt"
re.split( r'[\\/]', var )
['d:', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']

LinuxとMacのパスもサポートしたい場合は、filter(None、result)を追加するだけで、パスが「/」または「//」で始まるため、split()から不要な ''が削除されます。たとえば、「// mount / ...」または「/ var / tmp /」

import re    
var = "/var/stuff/morestuff/furtherdown/THEFILE.txt"
result = re.split( r'[\\/]', var )
filter( None, result )
['var', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']

1

os.path.split文字列を再帰的にすることができます

import os
def parts(path):
    p,f = os.path.split(path)
    return parts(p) + [f] if f else [p]

これをいくつかのパス文字列に対してテストし、パスを再構成して os.path.join

>>> for path in [
...         r'd:\stuff\morestuff\furtherdown\THEFILE.txt',
...         '/path/to/file.txt',
...         'relative/path/to/file.txt',
...         r'C:\path\to\file.txt',
...         r'\\host\share\path\to\file.txt',
...     ]:
...     print parts(path), os.path.join(*parts(path))
... 
['d:\\', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt'] d:\stuff\morestuff\furtherdown\THEFILE.txt
['/', 'path', 'to', 'file.txt'] /path\to\file.txt
['', 'relative', 'path', 'to', 'file.txt'] relative\path\to\file.txt
['C:\\', 'path', 'to', 'file.txt'] C:\path\to\file.txt
['\\\\', 'host', 'share', 'path', 'to', 'file.txt'] \\host\share\path\to\file.txt

リストの最初の要素は、ドライブ文字、UNCパス、および絶対パスと相対パスをどのように処理するかによって、異なる処理が必要になる場合があります。最後[p]を変更して[os.path.splitdrive(p)]、ドライブ文字とディレクトリのルートをタプルに分割することで問題を強制します。

import os
def parts(path):
    p,f = os.path.split(path)
    return parts(p) + [f] if f else [os.path.splitdrive(p)]

[('d:', '\\'), 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']
[('', '/'), 'path', 'to', 'file.txt']
[('', ''), 'relative', 'path', 'to', 'file.txt']
[('C:', '\\'), 'path', 'to', 'file.txt']
[('', '\\\\'), 'host', 'share', 'path', 'to', 'file.txt']

編集:私はこの答えは非常によく似ていることを認識している上に与えられたものuser1556435。パスのドライブコンポーネントの処理が異なるので、答えはお任せします。


0

他の人が説明したように-あなたの問題\は、文字列リテラル/定数のエスケープ文字であるを使用することから生じました。OTOH、別のソースからのファイルパス文字列がある場合(ファイル、コンソールから読み取られるか、os関数によって返されます)-'\\'またはr '\'での分割に問題はありませんでした。

そして、他の人が提案したように\、プログラムのリテラルで使用したい場合は、それを複製する\\か、リテラル全体の前にを付けるか、パーサーがそれを変換してCR(復帰)文字に変換しないrようにするr'lite\ral'必要r"lite\ral"\ありrます。

ただし、もう1つの方法があり\ます。コードでバックスラッシュパス名を使用しないでください。前世紀以降、Windowsは、スラッシュをディレクトリの区切り文字として使用するパス名を認識して正常に動作します/。どういうわけか多くの人はそれを知らない..しかしそれはうまくいく:

>>> var = "d:/stuff/morestuff/furtherdown/THEFILE.txt"
>>> var.split('/')
['d:', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']

ちなみに、これはコードがUnix、Windows、Macで動作するようにします。これらはすべて/、モジュールの定義済み定数を使用したくない場合でも、ディレクトリセパレータとして使用されるためですos


残念ながら、Pythonスクリプトから実行する別のプログラムからデータが返されます。「\」または「/」のどちらを使用するかを制御することはできません。これを決定するのはサードパーティのプログラムです(おそらくプラットフォームベース)。
BeeBand

@BeeBand:ああ、そうすれば、テスト中に文字列をプログラムでリテラルとして提供したときに経験した問題は発生しません。または、パスを受け取った後、次の邪悪なハックを実行できますvar = var.replace('\\','/')
。-

それは確かに邪悪なハックです:o)
BeeBand 2010

@BeeBand:それが私が警告した理由です。私は何かが悪であると言うとき、私は必ずしもそれを使用してはならないという意味ではありません-しかし、1つはなければならない非常に多く、彼らはそれと意図しない結果のアラートを使用している理由を注意してください。この場合、非常にありそうもない結果は、これがUnixファイルシステムで ``ファイル名またはディレクトリ名で使用(これは本当に難しいですが可能です)で使用される場合、このコードは「壊れる」ことになります
Nas Banov

0

次のようなfiledata.txt内容のファイルがあるとします。

d:\stuff\morestuff\furtherdown\THEFILE.txt
d:\otherstuff\something\otherfile.txt

ファイルパスを読み取って分割できます。

>>> for i in open("filedata.txt").readlines():
...     print i.strip().split("\\")
... 
['d:', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']
['d:', 'otherstuff', 'something', 'otherfile.txt']

これは確かに機能します、ありがとう!しかし、バックスラッシュをエスケープすることを心配したくないので、私はbroneのソリューションを選択しました。
BeeBand

9
ファイルシステムに依存するため、pythonicではありません。
jb。

0

os.path.basename関数を使用するため、次のように使用します。返されるリストにスラッシュを追加しません。また、プラットフォームのスラッシュ(ウィンドウの\\やunixの/など)でも機能します。さらに、Windowsがサーバーパスに使用する\\\\は追加されません。

def SplitPath( split_path ):
    pathSplit_lst   = []
    while os.path.basename(split_path):
        pathSplit_lst.append( os.path.basename(split_path) )
        split_path = os.path.dirname(split_path)
    pathSplit_lst.reverse()
    return pathSplit_lst

したがって、「\\\\ server \\ folder1 \\ folder2 \\ folder3 \\ folder4」の場合

あなたは得る

['サーバー'、 'フォルダー1'、 'フォルダー2'、 'フォルダー3'、 'フォルダー4']


1
これは、結果を渡してos.path.join()元の文字列を返すという不変式には従いません。あなたの例の入力の正しい出力はだと思います[r'\\','server','folder1','folder2','folder3','folder4']。すなわち何をos.path.split()します。
Jon-Eric

0

これが質問に完全に答えるかどうかは実際にはわかりませんが、スタックを保持し、os.pathベースの操作に固執し、項目のリスト/スタックを返すこの小さな関数を書いて楽しい時間を過ごしました。

  9 def components(path):
 10     ret = []
 11     while len(path) > 0:
 12         path, crust = split(path)
 13         ret.insert(0, crust)
 14
 15     return ret
 16

0

以下のコード行で処理できます。

  1. C:/パス/パス
  2. C://パス//パス
  3. C:\ path \ path
  4. C:\ path \ path

パス= re.split(r '[/// \]'、パス)


0

楽しみのために再帰します。

最もエレガントな答えではありませんが、どこでも機能するはずです。

import os

def split_path(path):
    head = os.path.dirname(path)
    tail = os.path.basename(path)
    if head == os.path.dirname(head):
        return [tail]
    return split_path(head) + [tail]

確かに、ごめんなさい。質問を注意深く読んでいる必要があります...「dos」パス
DuGNu

-1

使用する ntpath.split()


os.path.split()を使用すると、(d:\\stuffmorestuff\x0curtherdown\thefile.mux)が
返されます

BeeBandが指摘したように、os.path.split()は実際には望ましいことを行いません。
アンワインド

申し訳ありませんが、os.pathはあなたのOSに依存してのみ機能します。ntpathはdosパスを解析します。
deft_code

でもntpathと私はまだ取得d:\\stuff, morestuff\x0curtherdown\thefile.mux
BeeBand

2
@BeeBand:文字列のエスケープに問題があります。 '\x0c'フォームフィード文字です。フォームフィード文字を作成する方法は '\ f'です。リテラル文字列 '\ f'が本当に必要な場合は、'\\f'またはの2つのオプションがありますr'\f'
deft_code
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.