ทำไม 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 คือการป้องกันไม่ให้ใส่เครื่องหมายทับในเส้นทาง
S.Lott

6
แน่นอนปัญหากับ str.join () แน่นอนว่ามันจะไม่กำจัดเครื่องหมายทับสองครั้ง ฉันคิดว่านี่เป็นวัตถุประสงค์หลักสำหรับผู้ใช้ os.path.join เช่น '/'.join(uty'/etc/', '/ conf']) ให้ผลลัพธ์สามสแลช: '/ etc /// conf'
Dustin Rasener

17
@DustinRasener คุณสามารถใช้os.path.normpathเพื่อให้บรรลุเป้าหมายนั้น
Gareth Latty

5
เบาะแสไม่มีเหตุผลที่ผู้คนจะผิดหวังมากกว่าพฤติกรรม os.path.join ในภาษาอื่น ๆ ไลบรารี่ / เมธอดการเข้าร่วมเส้นทางที่เทียบเท่าจะทำงานเหมือนกันทุกประการ มันปลอดภัยและเหมาะสมกว่า
Don Cheadle

19
นี่เป็นเรื่องน่าหงุดหงิดเพราะมันเป็นเวทย์มนตร์โดยปริยายซึ่งตรงกันข้ามกับฮิวริสติกที่สำคัญของ "ชัดเจนดีกว่าโดยปริยาย" และมันก็เป็น นักออกแบบภาษาอาจเชื่อว่าพวกเขารู้ดีกว่า แต่มีเหตุผลที่ชัดเจนและปลอดภัยที่สามารถพิสูจน์ได้ว่าบางครั้งต้องการทำสิ่งนี้ ตอนนี้เราทำไม่ได้ นี่คือเหตุผลที่เราไม่สามารถมีสิ่งที่ดี
เซซิลแกงกะหรี่

151

แนวคิดของos.path.join()การทำให้โปรแกรมของคุณข้ามแพลตฟอร์ม (linux / windows / etc)

แม้แต่สแลชที่หักพังก็ยังมีอยู่

ดังนั้นก็เพียงทำให้รู้สึกเมื่อมีการใช้กับชนิดของจุดอ้างอิงเช่นบาง หรือ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

3
ใช้งานได้บน windows เช่นกัน (python 2.7.6) มันไม่ได้มีส่วนเกี่ยวข้องกับ 'C: \' และเข้าร่วมไดเรกทอรีย่อย
rickfoosusa

23

อย่าใช้ฟอร์เวิร์ดสแลชที่จุดเริ่มต้นของคอมโพเนนต์พา ธ ยกเว้นเมื่ออ้างถึงไดเร็กทอรีรูท:

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

ดูเพิ่มเติมที่: http://docs.python.org/library/os.path.html#os.path.join


21

ที่จะช่วยให้เข้าใจว่าทำไมพฤติกรรมที่น่าแปลกใจนี้ไม่ได้อย่างสิ้นเชิงสาหัสพิจารณาใบสมัครที่รับชื่อไฟล์ config เป็นอาร์กิวเมนต์นี้:

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

ช่วงเวลานี้เมื่อคุณต้องการวิธีการแก้ปัญหาที่หลายคนคิดว่าน่ากลัว
ashrasmun


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', '']

* ที่ด้านหน้าของรายการแบ่งแต่ละรายการของรายการพารามิเตอร์ของตัวเอง



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

ฉันขอแนะนำให้ตัดจากที่สองและสตริงต่อไปนี้สาย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
อัปเดต - ฉันคิดว่าคุณจะต้องให้คำแนะนำเป็นเส้นทางกันยายนคุณกำลังใช้ในท้องถิ่นเป็นอิสระจากระบบปฏิบัติการที่คุณกำลังทำงานอยู่
Neil McGill

1
ใช่. อีกทางเลือกหนึ่งสามารถแบ่งทั้งตัวเลือกที่ใช้กันทั่วไป ... แต่แล้วระบบปฏิบัติการอื่น ๆ อาจเกิดขึ้นกับหนึ่งในสาม
NichtJens
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.