この場合、なぜos.path.join()が機能しないのですか?


325

以下のコードは結合されません。デバッグすると、コマンドはパス全体ではなく最後のエントリのみを保存します。

os.path.join('/home/build/test/sandboxes/', todaystr, '/new_sandbox/')

これをテストすると/new_sandbox/、コードの一部のみが保存されます。

回答:


426

後者の文字列はスラッシュで始めるべきではありません。スラッシュで始まる場合は、「絶対パス」と見なされ、それより前のすべてが破棄されます。

Pythonドキュメントをos.path.join引用:

コンポーネントが絶対パスの場合、以前のすべてのコンポーネントは破棄され、結合は絶対パスコンポーネントから続行されます。

Windowsでは、ドライブ文字に関連する動作が、以前のバージョンのPythonと比較して変更されているようです。

Windowsでは、絶対パスコンポーネント(たとえばr'\foo')が検出されても、ドライブ文字はリセットされません。コンポーネントにドライブ文字が含まれている場合、以前のすべてのコンポーネントは破棄され、ドライブ文字がリセットされます。各ドライブos.path.join("c:", "foo")には現在のディレクトリがあるため、ではなく、ドライブC:c:foo)の現在のディレクトリからの相対パスを表すことに注意してくださいc:\foo


85
-1:文字列に「/」を含めることはできません。os.path.joinの重要な点の1つは、パスにスラッシュを入れないようにすることです。
S.Lott、2009

6
もちろん、str.join()の問題は、二重スラッシュを削除しないことです。これはos.path.joinを使用している人々の主な目的だと思います。たとえば、 '/'。join(['/ etc /'、 '/ conf'])は3つのスラッシュになります: '/ etc /// conf'
Dustin Rasener

17
@DustinRasener os.path.normpathその目的を達成するために使用できます。
Gareth Latty、2012年

5
人々がos.path.joinの振る舞いにいらいらしている理由はわかりません。他の言語では、同等のパス結合ライブラリ/メソッドはまったく同じように動作します。それはより安全で、より理にかなっています。
Don Cheadle

19
これは、「明示的であることが暗黙的であるよりも優れている」という基本的なヒューリスティックとは対照的に暗黙的な魔法であることに苛立たしいものです。そしてです。言語設計者は自分たちがよりよく知っていると信じているかもしれませんが、時々これをやりたいと思う明らかで安全な理由があります。今はできません。これが私たちが良いものを持つことができない理由です。
Cecil Curry

151

のアイデア os.path.join()、プログラムをクロスプラットフォーム(linux / windows / etc)にすることです。

たった1人のスラッシュでもそれを台無しにします。

したがって、os.environ['HOME']またはのようなある種の基準点で使用する場合にのみ意味があります os.path.dirname(__file__)


75

os.path.join()とともに使用してos.path.sep、相対パスではなく絶対パスを作成できます。

os.path.join(os.path.sep, 'home','build','test','sandboxes',todaystr,'new_sandbox')

8
os.path.sep絶対パスを構築するための最初の要素としてのの使用は、ここでの他のどの回答よりも優れています!os.path基本的なstrメソッドではなく、全体を使用するポイントは、記述を避けること/です。すべてのサブディレクトリを新しい引数として置き、すべてのスラッシュを削除することも素晴らしいです。todaystrスラッシュで始まらないチェックで確認することをお勧めします。;)
snooze92 2014年

3
これはWindowsでも機能します(python 2.7.6)。「C:\」と干渉せず、サブディレクトリに参加しました。
rickfoosusa、2015


21

この驚くべき動作がまったくひどくない理由を理解するのに役立つように、構成ファイル名を引数として受け入れるアプリケーションを検討してください。

config_root = "/etc/myapp.conf/"
file_name = os.path.join(config_root, sys.argv[1])

アプリケーションが次のように実行された場合:

$ myapp foo.conf

構成ファイル/etc/myapp.conf/foo.confが使用されます。

しかし、アプリケーションが次のように呼び出された場合にどうなるかを検討してください。

$ myapp /some/path/bar.conf

次に、設定ファイルを使用するmyapp 必要があります/some/path/bar.conf/etc/myapp.conf/some/path/bar.confまたは類似しない)。

それは素晴らしいことではないかもしれませんが、これが絶対パスの振る舞いの動機だと思います。


ありがとう!あなたの答えを読むまで、私はいつもこの振る舞いを嫌っていました!docs.python.org/3.5/library/os.path.html#os.path.joinに記載されていますが、その動機は記載されていません。
Eli_B 2017

多くの人がひどいと考えるソリューションを正確に必要とするこの瞬間。
アシュラムン

12

これ'/new_sandbox/'は、a /で始まるため、ルートディレクトリからの相対パスと見なされるためです。先頭を削除し/ます。


8

関数の移植性を高めるには、次のように使用します。

os.path.join(os.sep, 'home', 'build', 'test', 'sandboxes', todaystr, 'new_sandbox')

または

os.path.join(os.environ.get("HOME"), 'test', 'sandboxes', todaystr, 'new_sandbox')

8

コンボ試しsplit("/")*合流する既存との文字列にします。

import os

home = '/home/build/test/sandboxes/'
todaystr = '042118'
new = '/new_sandbox/'

os.path.join(*home.split("/"), todaystr, *new.split("/"))


使い方...

split("/") 既存のパスをリストに変換します: ['', 'home', 'build', 'test', 'sandboxes', '']

* リストの前で、リストの各項目を独自のパラメーターで分割します


3

new_sandboxのみでお試しください

os.path.join('/home/build/test/sandboxes/', todaystr, 'new_sandbox')

2

余分なスラッシュもなしに、このようにしてください

root="/home"
os.path.join(root,"build","test","sandboxes",todaystr,"new_sandbox")

0

を使用するos.path.join()と自動的に行われるドットが既に含まれている拡張機能を含める場合にも、同様の問題が発生する可能性があることに注意してくださいos.path.splitext()。この例では:

components = os.path.splitext(filename)
prefix = components[0]
extension = components[1]
return os.path.join("avatars", instance.username, prefix, extension)

たとえextensionかもしれない.jpgあなたは「foobarに」という名前のフォルダではなく、「foobar.jpg」と呼ばれるファイルで終わります。これを防ぐには、拡張子を個別に追加する必要があります。

return os.path.join("avatars", instance.username, prefix) + extension

0

あなたはできstripます'/'

>>> os.path.join('/home/build/test/sandboxes/', todaystr, '/new_sandbox/'.strip('/'))
'/home/build/test/sandboxes/04122019/new_sandbox'

0

2番目以降の文字列から文字列を取り除きos.path.sep、絶対パスとして解釈されないようにすることをお勧めします。

first_path_str = '/home/build/test/sandboxes/'
original_other_path_to_append_ls = [todaystr, '/new_sandbox/']
other_path_to_append_ls = [
    i_path.strip(os.path.sep) for i_path in original_other_path_to_append_ls
]
output_path = os.path.join(first_path_str, *other_path_to_append_ls)

0
os.path.join("a", *"/b".split(os.sep))
'a/b'

完全版:

import os

def join (p, f, sep = os.sep):
    f = os.path.normpath(f)
    if p == "":
        return (f);
    else:
        p = os.path.normpath(p)
        return (os.path.join(p, *f.split(os.sep)))

def test (p, f, sep = os.sep):
    print("os.path.join({}, {}) => {}".format(p, f, os.path.join(p, f)))
    print("        join({}, {}) => {}".format(p, f, join(p, f, sep)))

if __name__ == "__main__":
    # /a/b/c for all
    test("\\a\\b", "\\c", "\\") # optionally pass in the sep you are using locally
    test("/a/b", "/c", "/")
    test("/a/b", "c")
    test("/a/b/", "c")
    test("", "/c")
    test("", "c")

os.sepが実際には"\"どうなりますか?次に、最初の例はになりますos.path.join("a", *"/b".split("\\"))。これは"/b"...意図した結果ではないかと思います。
NichtJens

1
更新-ローカルで使用しているOSのパスと、実行しているOSのパスに関係なく、ヒントを与える必要があると思います
Neil McGill

1
はい。あるいは、一般的に使用される両方のオプションで分割することもできますが、他のいくつかのOSでは3番目のオプションが考えられます。
NichtJens
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.