Python os.path.join trên Windows


99

Tôi đang cố gắng học python và đang tạo một chương trình sẽ xuất ra một tập lệnh. Tôi muốn sử dụng os.path.join, nhưng khá bối rối. Theo tài liệu nếu tôi nói:

os.path.join('c:', 'sourcedir')

Tôi hiểu "C:sourcedir". Theo các tài liệu, điều này là bình thường, phải không?

Nhưng khi tôi sử dụng lệnh copytree, Python sẽ xuất nó theo cách mong muốn, ví dụ:

import shutil
src = os.path.join('c:', 'src')
dst = os.path.join('c:', 'dst')
shutil.copytree(src, dst)

Đây là mã lỗi tôi nhận được:

WindowsError: [Lỗi 3] Hệ thống không thể tìm thấy đường dẫn được chỉ định: 'C: \ src /*.*'

Nếu tôi quấn os.path.joinvới os.path.normpathtôi cũng gặp lỗi tương tự.

Nếu điều này os.path.joinkhông thể được sử dụng theo cách này, thì tôi bối rối về mục đích của nó.

Theo các trang do Stack Overflow đề xuất, không nên sử dụng dấu gạch chéo khi tham gia — điều đó đúng, tôi cho là vậy?

Câu trả lời:


59

Windows có một khái niệm về thư mục hiện tại cho mỗi ổ đĩa. Do đó, "c:sourcedir"có nghĩa là "nguồn gốc" bên trong thư mục C: \ hiện tại và bạn sẽ cần chỉ định một thư mục tuyệt đối.

Bất kỳ điều nào trong số này sẽ hoạt động và cho cùng một kết quả, nhưng tôi không có máy ảo Windows nào được kích hoạt vào lúc này để kiểm tra kỹ:

"c:/sourcedir"
os.path.join("/", "c:", "sourcedir")
os.path.join("c:/", "sourcedir")

8
os.path.join ('C: /', 'chuacedir') đã hoạt động như mong đợi. Tôi cảm ơn bạn rất nhiều. , Smashery và Roger Pate. Tôi nợ bạn :)
Frank E.

Xin lỗi, ngắt dòng không được lưu trong nhận xét, nó trông rất lộn xộn
Frank E.

Ngay cả khi điều này hoạt động trong một số trường hợp, câu trả lời của @AndreasT là một giải pháp tốt hơn nhiều. Sử dụng os.sep sẽ chọn giữa / và \ tùy thuộc vào hệ điều hành.
SenhorLucas

Có bất kỳ điểm nào trong việc sử dụng os.path.joinhoặc os.sepnếu bạn vẫn định chỉ định c:? c:không có ý nghĩa trên các hệ điều hành khác.
naught101

tất cả các giải pháp này chỉ đáp ứng một phần. Bạn có thể thêm dấu phân tách theo cách thủ công khi bạn có một trường hợp cụ thể, nhưng trong trường hợp bạn muốn thực hiện theo chương trình, tiêu chí nào os.path.join('c:','folder')hoạt động khác với tiêu chí os.path.join('folder','file')nào? Đó là vì :hay bởi vì 'c:' là một ổ đĩa?
Vincenzooo

121

Để thậm chí còn phức tạp hơn, câu trả lời nhất quán nhất của tài liệu python sẽ là:

mypath = os.path.join('c:', os.sep, 'sourcedir')

Vì bạn cũng cần os.sep cho đường dẫn gốc posix:

mypath = os.path.join(os.sep, 'usr', 'lib')

5
Xin lỗi vì sự thiếu hiểu biết của tôi - Có vẻ như mã vẫn khác nhau giữa Windows và Linux, vậy điều gì tạo nên os.sepsự vượt trội?
pianoJames

3
Hãy lưu ý điều này khi cố gắng tiêm os.sep. Nó chỉ hoạt động sau ký tự ổ đĩa trống. >>> os.path.join ("C: \ tạm biệt", os.sep, "temp") 'C: \\ temp'
Jobu

1
@pianoJames câu trả lời của tôi được xây dựng tắt của một này để cung cấp một giải pháp hệ thống-agnostic: stackoverflow.com/a/51276165/3996580
Scott Gigante

Tôi không hiểu điểm của tất cả các giải pháp "khổng lồ" này. os.sephữu ích khi bạn muốn thao tác các đường dẫn mà không cần đưa ra các giả định về dấu phân cách. Thật vô nghĩa khi sử dụng os.path.join()vì nó đã biết dấu phân cách phù hợp. Cũng vô nghĩa nếu cuối cùng bạn cần chỉ định rõ ràng thư mục gốc theo tên (như bạn có thể thấy trong ví dụ của riêng mình). Tại sao "c:" + os.septhay vì đơn giản "c:\\", hoặc os.sep + "usr"thay vì đơn giản "/usr"? Cũng lưu ý rằng trong Win shell, bạn không thể cd c:nhưng bạn có thể cd c:\ , gợi ý rằng tên gốc thực sự là như vậy c:\ .
Michael Ekoka

13

Lý do os.path.join('C:', 'src')không hoạt động như bạn mong đợi là do điều gì đó trong tài liệu mà bạn đã liên kết đến:

Lưu ý rằng trên Windows, vì có một thư mục hiện tại cho mỗi ổ đĩa, os.path.join ("c:", "foo") đại diện cho một đường dẫn liên quan đến thư mục hiện tại trên ổ C: (c: foo), không phải c : \ foo.

Như ghostdog đã nói, bạn có thể muốn mypath=os.path.join('c:\\', 'sourcedir')


12

Đối với một giải pháp bất khả tri hệ thống hoạt động trên cả Windows và Linux, bất kể đường dẫn đầu vào là gì, người ta có thể sử dụng os.path.join(os.sep, rootdir + os.sep, targetdir)

Trên WIndows:

>>> os.path.join(os.sep, "C:" + os.sep, "Windows")
'C:\\Windows'

Trên Linux:

>>> os.path.join(os.sep, "usr" + os.sep, "lib")
'/usr/lib'

1
Cảm ơn! Điều này thậm chí còn hữu ích hơn vì nó không bị lỗi gotcha mà @Jobu đã đề cập trước đó: os.path.join (os.sep, "C: \\ a" + os.sep, "b") trả về "C: \\ a \\ b "trên Windows.
pianoJames

1
Mặc dù vậy, một trong hai ví dụ này là hệ thống bất khả tri như thế nào? c:không tồn tại trên * nix và usrkhông tồn tại trên windows ..
naught101

Lời gọi hàm os.path.join(os.sep, rootdir + os.sep, targetdir)là hệ thống không khả thi chính xác bởi vì nó hoạt động với cả hai ví dụ cụ thể của hệ thống đó mà không cần thay đổi mã.
Scott Gigante

Giải pháp này, giống như bài đăng trước đó đã truyền cảm hứng cho nó, vẫn dựa vào việc thiết lập rootdir như thế nào rootdir = "usr" if nix else "c:". Nhưng các rootdir = "/usr" if nix else "c:\\"tác phẩm trực tiếp và chính xác hơn cũng vậy, không cần os.sepnhào lộn và gãi đầu sau đó. Không có gì nguy hiểm khi thư mục gốc trên * nix sẽ bắt đầu bằng bất kỳ thứ gì khác ngoài dấu gạch chéo lên, hoặc Windows sẽ có các thư mục gốc được đặt tên mà không có dấu hai chấm và dấu gạch chéo ngược (ví dụ: trong Win shell, bạn không thể làm như vậy cd c:, bạn cần chỉ định dấu gạch chéo ngược ở cuối), vậy tại sao lại giả vờ khác?
Michael Ekoka

11

Nói một cách phức tạp, có lẽ không tốt nếu hardcode hoặc / hoặc \ làm dấu phân cách đường dẫn. Có lẽ điều này sẽ là tốt nhất?

mypath = os.path.join('c:%s' % os.sep, 'sourcedir')

hoặc là

mypath = os.path.join('c:' + os.sep, 'sourcedir')

7

Tôi muốn nói rằng đây là một lỗi (cửa sổ) python.

Tại sao lỗi?

Tôi nghĩ rằng tuyên bố này nên True

os.path.join(*os.path.dirname(os.path.abspath(__file__)).split(os.path.sep))==os.path.dirname(os.path.abspath(__file__))

Nhưng nó là Falsetrên máy tính windows.


1
Tôi có xu hướng đồng ý rằng điều đó tạo thành một lỗi Python. đây vẫn là trường hợp? ( Viết từ tương lai không tưởng huy hoàng vào cuối năm 2015. )
Cecil Curry

Tôi không thể trả lời câu hỏi này liên quan đến windows, vì tôi không có quyền truy cập vào máy windows, nhưng tôi đoán hành vi của python liên quan đến câu hỏi này không thay đổi. Dù sao, câu lệnh này cũng không đúng đối với các triển khai Linux, vì câu lệnh đầu tiên trả về đường dẫn không có dấu phân tách đứng đầu (hay còn gọi là thư mục gốc), trong khi câu lệnh thứ hai trả về đường dẫn bao gồm dấu phân cách đứng đầu.
georg

Vì vậy, tôi thực sự không thích câu trả lời của tôi về câu hỏi này nữa. Nhưng tôi cũng không thích hành vi của python liên quan đến điều này.
georg

@Cecil Tôi đang đặt câu hỏi này vì cùng một vấn đề ... có vẻ như vẫn xảy ra trường hợp này.
joshmcode

5

để tham gia đường dẫn windows, hãy thử

mypath=os.path.join('c:\\', 'sourcedir')

về cơ bản, bạn sẽ cần phải thoát khỏi vết chém


4

Bạn có một số cách tiếp cận để xử lý đường dẫn trên Windows, từ những đường dẫn được mã hóa cứng nhất (như sử dụng ký tự chuỗi thô hoặc dấu gạch chéo ngược thoát) đến những đường dẫn ít nhất. Dưới đây là một số ví dụ sẽ hoạt động như mong đợi. Sử dụng những gì phù hợp hơn với nhu cầu của bạn.

In[1]: from os.path import join, isdir

In[2]: from os import sep

In[3]: isdir(join("c:", "\\", "Users"))
Out[3]: True

In[4]: isdir(join("c:", "/", "Users"))
Out[4]: True

In[5]: isdir(join("c:", sep, "Users"))
Out[5]: True

0

Đồng ý với @ georg-

Sau đó tôi sẽ nói tại sao chúng ta cần khập khiễng os.path.join- tốt hơn nên sử dụng str.joinhoặc unicode.joinví dụ:

sys.path.append('{0}'.join(os.path.dirname(__file__).split(os.path.sep)[0:-1]).format(os.path.sep))

2
yeah, đúng, nó rõ ràng hơn theo cách đó. Tại sao không sử dụng regexes khi bạn đang ở đó? hay gọi một tập lệnh perl và xử lý đầu ra?
Jean-François Fabre

Tôi không nghĩ đó là một ý kiến ​​hay vì os.path.join có ngữ nghĩa khá tốt ... Vì vậy, bạn nhìn thấy nó trong một đoạn mã và hiểu ngay điều gì đang xảy ra.
SenhorLucas

0

trả lời nhận xét của bạn: "the others '//' 'c:', 'c: \\' không hoạt động (C: \\ đã tạo hai dấu gạch chéo ngược, C: \ hoàn toàn không hoạt động)"

Trên các cửa sổ đang sử dụng os.path.join('c:', 'sourcedir') sẽ tự động thêm hai dấu gạch chéo ngược \\vào trước dấu ngoặc kép .

Để giải quyết đường dẫn, vì python cũng hoạt động trên cửa sổ với dấu gạch chéo về phía trước -> '/' , chỉ cần thêm .replace('\\','/')bằng os.path.joinnhư sau: -

os.path.join('c:\\', 'sourcedir').replace('\\','/')

ví dụ: os.path.join('c:\\', 'temp').replace('\\','/')

đầu ra: 'C: / temp'


0

Các giải pháp được đề xuất rất thú vị và cung cấp một tài liệu tham khảo tốt, tuy nhiên chúng chỉ đáp ứng một phần. Bạn có thể thêm dấu phân tách theo cách thủ công khi bạn có một trường hợp cụ thể hoặc bạn biết định dạng của chuỗi đầu vào, nhưng có thể có trường hợp bạn muốn thực hiện theo chương trình trên các đầu vào chung.

Với một chút thử nghiệm, tôi tin rằng tiêu chí là dấu phân cách đường dẫn không được thêm vào nếu phân đoạn đầu tiên là ký tự ổ đĩa, nghĩa là một ký tự đơn theo sau là dấu hai chấm, bất kể nó có tương ứng với một đơn vị thực hay không.

Ví dụ:

import os
testval = ['c:','c:\\','d:','j:','jr:','data:']

for t in testval:
    print ('test value: ',t,', join to "folder"',os.path.join(t,'folder'))
test value:  c: , join to "folder" c:folder
test value:  c:\ , join to "folder" c:\folder
test value:  d: , join to "folder" d:folder
test value:  j: , join to "folder" j:folder
test value:  jr: , join to "folder" jr:\folder
test value:  data: , join to "folder" data:\folder

Một cách thuận tiện để kiểm tra tiêu chí và áp dụng hiệu chỉnh đường dẫn có thể là sử dụng os.path.splitdriveso sánh phần tử được trả về đầu tiên với giá trị thử nghiệm, chẳng hạn như t+os.path.sep if os.path.splitdrive(t)[0]==t else t.

Kiểm tra:

for t in testval:
    corrected = t+os.path.sep if os.path.splitdrive(t)[0]==t else t
    print ('original: %s\tcorrected: %s'%(t,corrected),' join corrected->',os.path.join(corrected,'folder'))
original: c:    corrected: c:\  join corrected-> c:\folder
original: c:\   corrected: c:\  join corrected-> c:\folder
original: d:    corrected: d:\  join corrected-> d:\folder
original: j:    corrected: j:\  join corrected-> j:\folder
original: jr:   corrected: jr:  join corrected-> jr:\folder
original: data: corrected: data:  join corrected-> data:\folder

nó có thể được cải thiện để mạnh mẽ hơn cho các dấu cách ở cuối và tôi đã thử nghiệm nó chỉ trên cửa sổ, nhưng tôi hy vọng nó cho một ý tưởng. Xem thêm Os.path: bạn có thể giải thích hành vi này không? để biết chi tiết thú vị về các hệ thống khác sau đó là cửa sổ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.