Bu durumda os.path.join () neden çalışmıyor?


325

Aşağıdaki kod birleştirilmez, hata ayıklandığında komut tüm yolu değil, yalnızca son girişi saklar.

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

Bunu test ettiğimde sadece /new_sandbox/kodun bir kısmını saklar .

Yanıtlar:


426

Sonraki dizeler eğik çizgi ile başlamamalıdır. Bir eğik çizgi ile başlarlarsa, o zaman "mutlak yol" olarak kabul edilirler ve onlardan önceki her şey atılır.

Python belgelerindenos.path.join alıntı yapmak :

Bir bileşen mutlak bir yolsa, önceki tüm bileşenler atılır ve birleştirme mutlak yol bileşeninden devam eder.

Windows'ta, önceki Python sürümlerine kıyasla değişmiş gibi görünen sürücü harfleriyle ilgili davranışı not edin:

Windows'ta, mutlak bir yol bileşeniyle (örn r'\foo'.) Karşılaşıldığında sürücü harfi sıfırlanmaz . Bir bileşen bir sürücü harfi içeriyorsa, önceki tüm bileşenler atılır ve sürücü harfi sıfırlanır. Her sürücü os.path.join("c:", "foo")için geçerli bir dizin bulunduğundan, sürücüdeki C:( c:foo) geçerli dizine göre bir yolu temsil ettiğine dikkat edin c:\foo.


85
-1: Hiçbir dize "/" içermemelidir. Os.path.join öğesinin bir bütün noktası, yola eğik çizgi girmesini önlemektir.
S.Lott

6
Str.join () ile ilgili sorun, elbette, çift eğik çizgileri ortadan kaldırmayacağıdır. Sanırım os.path.join kullanan kişiler için birincil amaç bu. örneğin '/'.join(['/etc/', '/ conf']) üç eğik çizgi ile sonuçlanır: '/ etc /// conf'
Dustin Rasener

17
@DustinRasener Bu os.path.normpathamaca ulaşmak için kullanabilirsiniz .
Gareth Latty

5
insanların neden os.path.join davranışı üzerinde hayal kırıklığına uğradığına dair hiçbir ipucu yok. Diğer dillerde, eşdeğer yol birleştirme kitaplığı / yöntemi tam olarak aynı şekilde davranır. Daha güvenli ve daha mantıklı.
Don Cheadle

19
Bunun nedeni bu sinir bozucu olduğunu örtük büyü , aksine kardinal sezgisel ait "Açık iyi örtülü aşıyor." Ve bu olduğunu . Dil tasarımcıları daha iyi bildiklerine inanabilirler, ancak bazen bunu yapmak için bariz ve açıkça güvenli nedenler vardır. Şimdi yapamayız. Bu yüzden iyi şeylere sahip olamayız.
Cecil Curry

151

Ana fikri os.path.join()programınızı çapraz platform haline getirmektir (linux / windows / etc).

Bir eğik çizgi bile onu mahveder.

Bu yüzden os.environ['HOME']veya gibi bir referans noktası ile kullanıldığında mantıklıdır os.path.dirname(__file__).


75

os.path.join()os.path.sepgöreli yol yerine mutlak yol oluşturmak için ile birlikte kullanılabilir .

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

8
os.path.sepMutlak bir yol oluşturmak için ilk eleman olarak kullanılması, burada diğer tüm cevaplardan daha iyidir! os.pathTemel str yöntemlerinden ziyade tüm kullanım noktası yazmamaktır /. Her alt dizini yeni bir argüman olarak koymak ve tüm eğik çizgileri kaldırmak da harika. todaystrEğik çizgi ile başlamayan bir çekten emin olmak iyi bir fikir olabilir ! ;)
snooze92

3
Bu pencerelerde de çalışır (python 2.7.6). 'C: \' ile etkileşime girmedi ve alt dizinlere katıldı.
rickfoosusa


21

Bu şaşırtıcı davranışın neden tamamen korkunç olmadığını anlamaya yardımcı olması için , yapılandırma dosyası adını bağımsız değişken olarak kabul eden bir uygulamayı düşünün:

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

Uygulama şununla yürütülürse:

$ myapp foo.conf

Yapılandırma dosyası /etc/myapp.conf/foo.confkullanılacaktır.

Ancak uygulama ile çağrılırsa ne olacağını düşünün:

$ myapp /some/path/bar.conf

Sonra myapp gerektiğini de yapılandırma dosyasını kullanmak /some/path/bar.conf(ve /etc/myapp.conf/some/path/bar.confveya benzeri).

Harika olmayabilir, ama bunun mutlak yol davranışı için motivasyon olduğuna inanıyorum.


Teşekkürler! Cevabınızı okuyana kadar bu davranıştan hep nefret etmiştim! Docs.python.org/3.5/library/os.path.html#os.path.join'de belgelenmiştir , ancak bunun motivasyonu yoktur.
Eli_B

Bu an, tam olarak çözüme ihtiyaç duyduğunuzda, birçok insan korkunç kabul ediyor.
ashrasmun

12

Çünkü '/new_sandbox/'a ile başlar /ve böylece kök dizine göreceli olduğu varsayılır. Ucu çıkarın /.


8

İşlevinizi daha taşınabilir hale getirmek için aşağıdaki gibi kullanın:

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

veya

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

8

Varolan birleştirmeler içeren dizeleri split("/")ve *dizelerini birleştirmeyi deneyin .

import os

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

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


Nasıl çalışır...

split("/") mevcut yolu listeye dönüştürür: ['', 'home', 'build', 'test', 'sandboxes', '']

* listenin önündeki listedeki her öğeyi kendi parametresi olarak ayırır


3

new_sandboxSadece dene

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

2

ekstra eğik çizgiler olmadan bunu böyle yap

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

0

os.path.join()Zaten nokta içeren bir uzantı eklemek için kullandığınızda benzer bir sorunun sizi ısıtabileceğini unutmayın ; bu, kullandığınızda otomatik olarak gerçekleşir os.path.splitext(). Bu örnekte:

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

Olsa extensionolabileceğini .jpgsiz "filanca" yerine "foobar.jpg" adlı bir dosya adında bir klasör ile bitirmek. Bunu önlemek için uzantıyı ayrıca eklemeniz gerekir:

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

0

yapabilirsiniz :strip'/'

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

0

Ben os.path.sepmutlak yollar olarak yorumlanmasını önleyerek , dizeyi ikinci ve aşağıdaki dizeleri şerit tavsiye ederim :

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'

daha dolgun bir sürüm:

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")

Ya os.sep aslında ise "\"? Sonra ilk örneğiniz olur os.path.join("a", *"/b".split("\\")), bu da "/b"sonuç verir ... Kuşkusuz amaçlanan sonuç budur.
NichtJens

1
Güncellenmiş - Sanırım üzerinde çalıştığınız işletim sisteminin yerel olarak bağımsız olarak kullandığınız sep sep olarak bir ipucu vermek zorunda
Neil McGill

1
Evet. Alternatif olarak, biri yaygın olarak kullanılan her iki seçeneğe de bölünebilir ... ancak daha sonra başka bir işletim sistemi üçüncü olabilir.
NichtJens
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.