Python os.path.join у Windows


98

Я намагаюся вивчити python і створюю програму, яка видасть скрипт. Я хочу використовувати os.path.join, але я досить розгублений. Згідно з документами, якщо я кажу:

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

Я розумію "C:sourcedir". Згідно з документами, це нормально, так?

Але коли я використовую команду copytree, Python видасть її бажаним способом, наприклад:

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

Ось код помилки, який я отримую:

Помилка Windows: [Помилка 3] Система не може знайти вказаний шлях: 'C: src /*.*'

Якщо я обернути os.path.joinз os.path.normpathя отримую ту ж помилку.

Якщо це os.path.joinне можна використовувати таким чином, то я збентежений щодо його призначення.

Відповідно до сторінок, запропонованих Stack Overflow, скісні риски не слід використовувати для об’єднання - це, я гадаю, правильно?

Відповіді:


59

Windows має концепцію поточного каталогу для кожного диска. Через це "c:sourcedir"означає "Sourcedir" у поточному каталозі C: ​​і вам потрібно буде вказати абсолютний каталог.

Будь-яка з них повинна працювати і давати однаковий результат, але на даний момент у мене не спрацьовує віртуальна машина Windows для повторної перевірки:

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

8
os.path.join ('C: /', 'sourcedir') працював належним чином. Дуже дякую, добрий сер :) інші '//' 'c:' 'c: \\' не працювали (C: \\ створив дві зворотні скісні риски, C: \ не працював взагалі) Ще раз спасибі ghostdog74 , Смашері та Роджер Пейт. Я в вашому боргу :)
Френк Е.

На жаль, розриви рядків не залишалися в коментарях, це виглядає дуже безладно
Френк Е.

Навіть якщо це працює в деяких випадках, відповідь @AndreasT є набагато кращим рішенням. За допомогою os.sep можна вибирати між / та \ залежно від ОС.
SenhorLucas

Чи є сенс використовувати os.path.joinабо os.sepякщо ви все c:одно збираєтесь вказати ? c:не має сенсу для інших ОС.
naught101

всі ці рішення задовольняють лише частково. Добре додавати роздільник вручну, коли у вас є окремий конкретний випадок, але якщо ви хочете зробити це програмно, то які критерії os.path.join('c:','folder')працюють інакше, ніж os.path.join('folder','file')? Це через, :чи тому, що 'c:' - це диск?
Vincenzooo

121

Щоб бути ще більш педантичним, найбільш послідовною відповіддю на python doc буде:

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

Оскільки вам також потрібен os.sep для кореневого шляху posix:

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

4
Вибачте за незнання. Здається, код все ще відрізняється між Windows і Linux, то що робить os.sepчудовим?
pianoJames

3
Зверніть увагу на цей снафу при спробі зробити ін’єкцію os.sep. Він працює лише після оголеної букви. >>> os.path.join ("C: \ до побачення", os.sep, "temp") 'C: \\ temp'
Jobu,

1
@pianoJames моя відповідь будується на цій, щоб надати системно-агностичне рішення: stackoverflow.com/a/51276165/3996580
Скотт Гіганте

Я не розумію сенсу всіх цих "педантичних" рішень. os.sepкорисний, коли ви хочете маніпулювати шляхами, не роблячи припущень про роздільник. Безглуздо використовувати, os.path.join()оскільки він уже знає правильний роздільник. Також безглуздо, якщо вам в кінцевому підсумку потрібно чітко вказати кореневий каталог за іменем (як ви можете бачити на власному прикладі). Чому "c:" + os.sepзамість просто "c:\\", чи os.sep + "usr"замість просто "/usr"? Також зауважте, що в оболонках Win ви не можете, cd c:але можете cd c:\ , припускаючи, що кореневе ім’я насправді c:\ .
Майкл Екока,

13

Причина os.path.join('C:', 'src')не працює, як ви очікуєте, через щось у документації, на яку ви зв’язали:

Зверніть увагу, що в Windows, оскільки для кожного диска існує поточний каталог, os.path.join ("c:", "foo") представляє шлях відносно поточного каталогу на диску C: (c: foo), а не c : \ foo.

Як сказав ghostdog, ви, мабуть, хочете mypath=os.path.join('c:\\', 'sourcedir')


12

Для системно-агностичного рішення, яке працює як у Windows, так і в Linux, незалежно від того, який шлях введення можна використовувати os.path.join(os.sep, rootdir + os.sep, targetdir)

На вікнах:

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

У Linux:

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

1
Дякую! Це ще корисніше, оскільки воно не страждає від того, що @Jobu згадував раніше: os.path.join (os.sep, "C: \\ a" + os.sep, "b") повертає "C: \\ a \\ b "у Windows.
pianoJames

1
Як один із цих прикладів, однак, агностичний щодо системи? c:не існує на * nix і usrне існує на вікнах ..
naught101

Виклик функції os.path.join(os.sep, rootdir + os.sep, targetdir)є агностичним для системи саме тому, що він працює з обома цими прикладами, специфічними для системи, без необхідності змінювати код.
Скотт

Це рішення, подібно до попередньої публікації, яка його надихнула, все ще покладається на встановлення типу rootdir rootdir = "usr" if nix else "c:". Але більш прямий і точний rootdir = "/usr" if nix else "c:\\"працює так само добре, без os.sepакробатики та подальших подряпин на голові. Немає небезпеки, що кореневий каталог на * nix почнеться з чого-небудь іншого, окрім косої риски, або що Windows матиме кореневі каталоги з іменами без кінцевої двокрапки та зворотної косої риски (наприклад, у оболонках Win ви не можете просто зробити cd c:, потрібно вказати кінцеву зворотну скісну риску), так чому робити вигляд інакше?
Майкл Екока,

11

Щоб бути педантичним, мабуть, не годиться жорстко кодувати або / або \ як роздільник шляхів. Може, це було б найкраще?

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

або

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

7

Я б сказав, що це помилка (Windows) python.

Чому помилка?

Я думаю, що це твердження повинно бути True

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

Але це Falseна машинах Windows.


1
Я схильний погодитися з тим, що це є помилкою Python. Це все ще так? ( Написано зі славетного утопічного майбутнього кінця 2015 року. )
Сесіл Каррі

Я не можу відповісти на це питання щодо вікон, оскільки я не маю доступу до машини Windows, але, мабуть, поведінка python щодо цього питання не змінилася. У будь-якому випадку, це твердження також не відповідає діям реалізацій Linux, оскільки перший оператор повертає шлях без провідного роздільника (він же кореневий каталог), тоді як другий вираз повертає шлях, що включає провідний роздільник.
georg

Тому мені насправді більше не подобається моя відповідь щодо цього питання. Але мені також не подобається поведінка python щодо цього.
georg

@Cecil Я зараз займаюся цим питанням через ту саму проблему ... схоже, це все ще так.
joshmcode

5

щоб приєднатися до шляху до Windows, спробуйте

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

в основному, вам потрібно буде уникнути скісної риски


4

У вас є декілька можливих підходів до обробки шляхів у Windows, від найбільш жорстко закодованих (як використання необроблених літеральних рядків або екрануючих зворотних скісних рисок) до найменших. Тут наведено кілька прикладів, які працюватимуть, як очікувалося. Використовуйте те, що краще відповідає вашим потребам.

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

Згода з @ georg-

Тоді я б сказав, навіщо нам потрібні кульгаві os.path.join- краще використовувати str.joinчи unicode.joinнаприклад

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

2
так, так, це waaaaayy зрозуміліше таким чином. Чому б не використовувати регулярні вирази, поки ви це робите? або викликати скрипт perl і обробити вихідні дані?
Жан-Франсуа Фабр

Я не думаю, що це хороша ідея, оскільки os.path.join - це досить хороша семантика ... Отже, ви бачите це в коді і відразу розумієте, що відбувається.
SenhorLucas

0

відповідаючи на ваш коментар: "інші '//' 'c:', 'c: \\' не працювали (C: \\ створив дві зворотні скісні риски, C: \ не працював взагалі)"

У вікнах за допомогою os.path.join('c:', 'sourcedir') буде автоматично додано дві зворотні скісні риски \\перед джерелом .

Для того, щоб визначити шлях, так як пітон працює на вікнах і з косою рисою -> «/» , просто додайте .replace('\\','/')з , os.path.joinяк показано нижче: -

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

наприклад: os.path.join('c:\\', 'temp').replace('\\','/')

вихід: 'C: / temp'


0

Запропоновані рішення цікаві та пропонують хороші рекомендації, проте вони задовольняють лише частково. Добре додавати роздільник вручну, коли у вас є окремий конкретний випадок або ви знаєте формат вхідного рядка, але бувають випадки, коли ви хочете зробити це програмно на загальних входах.

Трохи експериментуючи, я вважаю, що критерієм є те, що роздільник шляху не додається, якщо перший сегмент є буквою диска, що означає одну букву, за якою слідує двокрапка, незалежно від того, чи відповідає вона реальній одиниці.

Наприклад:

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

Зручним способом перевірити критерії та застосувати корекцію шляху може бути використання os.path.splitdriveпорівняння першого поверненого елемента із тестовим значенням, наприклад t+os.path.sep if os.path.splitdrive(t)[0]==t else t.

Тест:

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

його, можливо, можна вдосконалити, щоб він був більш надійним для кінцевих просторів, і я протестував його лише на вікнах, але, сподіваюся, це дає ідею. Дивіться також Os.path: чи можете ви пояснити таку поведінку? для цікавих деталей про системи, що не є вікнами.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.