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.