Як розділити Dos шлях на його компоненти в Python


153

У мене є змінна струна, яка представляє dos-шлях, наприклад:

var = "d:\stuff\morestuff\furtherdown\THEFILE.txt"

Я хочу розділити цей рядок на:

[ "d", "stuff", "morestuff", "furtherdown", "THEFILE.txt" ]

Я спробував з допомогою split()і , replace()але вони або обробляти тільки перший символ зворотний косу риску або вони вставляють шестигранний число в рядок.

Мені потрібно якось перетворити цю рядкову змінну в необроблену рядок, щоб я міг її розібрати.

Який найкращий спосіб зробити це?

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


6
Переглядаючи ці відповіді, пам’ятайте, що os.path.splitце не працює для вас, тому що ви не уникаєте цієї строки належним чином.
Джед Сміт

Вам потрібно вийти з рядка або використовувати rawstring: r"d:\stuff\morestuff\furtherdown\THEFILE.txt"щоб запобігти \sнеправильному тлумаченню таких речей .
smci

Відповіді:


164

Мене багато разів вкусили люди, які пишуть свої власні функції, і вони неправильно розуміють. Пробіли, косі риски, косої риси, колонки - можливості для плутанини не безмежні, але помилки легко робити все одно. Тому я є стикером для використання os.path, і рекомендую його на цій основі.

(Однак шлях до чесноти - це не найпростіший шлях, і багато людей, виявляючи це, спокушаються скотитися слизькою стежкою прямо до прокляття. Вони не зрозуміють, поки одного дня все не розпадеться, і вони - або , швидше за все, хтось інший - повинен розібратися, чому все пішло не так, і виявляється, хтось створив ім’я файлу, в якому змішуються косої риси та зворотні косої риси, - а хтось припускає, що відповідь "не робити цього". Дон " не будь хто з цих людей. За винятком того, хто змішував косі риси і нахили - ти може бути ними, якщо хочеш.)

Ви можете отримати диск та файл + файл таким чином:

drive, path_and_file = os.path.splitdrive(path)

Отримайте шлях і файл:

path, file = os.path.split(path_and_file)

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

folders = []
while 1:
    path, folder = os.path.split(path)

    if folder != "":
        folders.append(folder)
    else:
        if path != "":
            folders.append(path)

        break

folders.reverse()

(Це з'являється "\"на початку, foldersякщо шлях спочатку був абсолютним. Ви можете втратити трохи коду, якщо цього не хочете.)


@brone - я вважаю за краще використовувати це рішення, ніж турбуватися про те, щоб уникнути нахилу. Дякую!
BeeBand

1
Я був би радий, що він виявився невірним, але мені здається, що запропоноване рішення не працює, якщо використовується такий шлях, як цей "C: \ usr \ rs0 \ my0 \ in111102.log" (якщо тільки початковий ввід не є необробленим рядком )?
shearichard

1
Схоже, це не буде належним чином розділити шлях, якщо він містить лише каталог в OSX, такий як "/ path / to / my / folder /", щоб досягти того, що ви хочете додати ці два рядки до початку: if path.endswith("/"):і path = path[:-1].
Кевін Лондон

1
Я вважаю за краще рішення @Tompa
jaycode

1
Я згоден з jaycode : Tompa «s рішення є канонічний підхід і повинен був загальноприйнятий відповідь. Ця надмірно складна, неефективна альтернатива, не схильна до помилок, не може передати виробничий код. Там немає ні розумної причини , щоб спробувати (... і не вийде , звичайно) итеративно розібрати на частини , коли імена шляхів проста рядок розщеплення вдається тільки з одного рядка коду.
Сесіль Карі

287

Я б робив

import os
path = os.path.normpath(path)
path.split(os.sep)

Спочатку нормалізуйте рядок шляху у відповідний рядок для ОС. Тоді він os.sepповинен бути безпечним для використання в якості роздільника при розділенні функції рядків.


25
Єдина правдива відповідь: вона з’явилася . Канонічне рішення, звичайно, найпростіше. Ось! Бо він елегантний і невимушений і не має нестерпних крайових шаф.
Сесіль Карі

20
Як однолінійний,os.path.normpath(a_path).split(os.path.sep)
Даніель Фаррелл

2
Схоже, це не працює для path = root. У цьому випадку результатом path.split є ['', '']. Насправді це рішення split () дає крайній лівий каталог із назвою порожнього рядка (який може бути замінений відповідним косою рисою). Основна проблема полягає в тому, що одна косою рисою (вперед або назад залежно від ОС) є ім'ям кореневого каталогу, тоді як в інших місцях шляху це роздільник .
gwideman

2
Чи буде це краще працювати з lstrip тоді? os.path.normpath(path).lstrip(os.path.sep).split(os.path.sep)
Відар

1
@ user60561 Це тому, що в Linux зворотна косою рисою є дозволеним символом у назви файлів файлів, тоді як у Windows пряма коса риса не є. Ось чому в Windows normpathбуде розпізнавати косу рису в якості роздільника. В Linux normpathбуде просто припускати, що у вас є названий каталог \1\2і всередині нього називається файл або каталог 3.
Воєслав Стойкович

81

Ви можете просто використовувати найбільш пітонічний підхід (ІМХО):

import os

your_path = r"d:\stuff\morestuff\furtherdown\THEFILE.txt"
path_list = your_path.split(os.sep)
print path_list

Що дасть вам:

['d:', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']

Підказкою тут є використання os.sepзамість '\\'або'/' , оскільки це робить його системою незалежною.

Щоб видалити двокрапку з букви диска (хоча я не бачу жодної причини, чому ви хотіли б це зробити), ви можете написати:

path_list[0] = path_list[0][0]

22
Це працює some times. В інший час (як мінімум на вікнах) ви знайдете такі доріжки folder\folder2\folder3/file.txt. Краще спочатку нормалізувати (os.path.normpath) шлях, а потім розділити його.
vikki

7
Ця відповідь майже не була. Як підказує вікі , невдало нормалізувати назви шляхів перед розбиттям заклинань на заклинання в звичайних крайових випадках (наприклад, /foo//bar). Див Tompa «s відповідь на більш надійне рішення.
Сесіль Карі

62

У Python> = 3.4 це стало набагато простіше. Тепер ви можете використовуватиpathlib.Path.parts для отримання всіх частин шляху.

Приклад:

>>> from pathlib import Path
>>> Path('C:/path/to/file.txt').parts
('C:\\', 'path', 'to', 'file.txt')
>>> Path(r'C:\path\to\file.txt').parts
('C:\\', 'path', 'to', 'file.txt')

При встановленні Windows Python 3 це передбачає, що ви працюєте з шляхами Windows, а на * nix буде припускати, що ви працюєте з контурами posix. Зазвичай це те, що ви хочете, але якщо це не так, ви можете використовувати класи pathlib.PurePosixPathабо pathlib.PureWindowsPathв міру необхідності:

>>> from pathlib import PurePosixPath, PureWindowsPath
>>> PurePosixPath('/path/to/file.txt').parts
('/', 'path', 'to', 'file.txt')
>>> PureWindowsPath(r'C:\path\to\file.txt').parts
('C:\\', 'path', 'to', 'file.txt')
>>> PureWindowsPath(r'\\host\share\path\to\file.txt').parts
('\\\\host\\share\\', 'path', 'to', 'file.txt')

Редагувати: Існує також резервний порт на python 2: pathlib2


3
Path.parts - те, чого я завжди хотів, але ніколи не знав, що воно існувало до сьогодні.
JamEnergy

чому це не було обгорнуте приємною функцією рідного пітона?
Едуардо Піньятелі

2
ЦЕ відповідь!
найріз

11

Проблема тут починається з того, як ви створюєте рядок в першу чергу.

a = "d:\stuff\morestuff\furtherdown\THEFILE.txt"

Зроблено так, Python намагається окремий випадок це: \s, \m, \f, і \T. У вашому випадку \fдо цього поводжуться як з подачею формату (0x0C), тоді як з іншими косою рисою обробляються правильно. Що вам потрібно зробити, це одне з таких:

b = "d:\\stuff\\morestuff\\furtherdown\\THEFILE.txt"      # doubled backslashes
c = r"d:\stuff\morestuff\furtherdown\THEFILE.txt"         # raw string, no doubling necessary

Потім, розділивши будь-яке з них, ви отримаєте бажаний результат.


@W. Craig Trader - дякую, але цей шлях не той, який я генерую сам - він повертається до мене з іншої програми, і я повинен зберігати ці дані в змінній. Я не впевнений, як перетворити дані, що зберігаються в змінній, в "необроблений текст".
BeeBand

Не існує такого поняття, як "необроблений текст" ... це лише те, як ви його представляєте в джерелі. Або додайте r "" до рядка, або передайте його через .replace ('\\', '/')
Marco Mariani

@BeeBand, як ти повертаєш дані з іншої програми? Читаєте ви з файлу, труби, розетки? Якщо так, то вам не потрібно робити нічого фантазійного; Єдина причина подвоєння косої риски або використання необмежених рядків полягає в розміщенні рядкових констант у код Python. З іншого боку, якщо інша програма генерує подвійні косі нахили, тоді ви хочете очистити це перед тим, як розділити шлях.
Craig Trader

@W. Craig Trader - я читаю його з файлу, який записує інша програма. Я не міг чомусь дістатись split()або replace()працювати - я продовжував отримувати шестигранні значення. Ти маєш рацію, але, я думаю, що я гавкнув неправильне дерево із сирою ідеєю струни - я думаю, що я просто split()неправильно використовував . Тому що я спробував деякі з цих рішень за допомогою, split()і вони працюють для мене зараз.
BeeBand

10

Для дещо більш стислого рішення врахуйте наступне:

def split_path(p):
    a,b = os.path.split(p)
    return (split_path(a) if len(a) and len(b) else []) + [b]

Це моє улюблене рішення цієї проблеми. Дуже хороша.
Буде Мур

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

4

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


4

Функціональний спосіб, з генератором .

def split(path):
    (drive, head) = os.path.splitdrive(path)
    while (head != os.sep):
        (head, tail) = os.path.split(head)
        yield tail

Дія:

>>> print([x for x in split(os.path.normpath('/path/to/filename'))])
['filename', 'to', 'path']

3

Це працює для мене:

>>> a=r"d:\stuff\morestuff\furtherdown\THEFILE.txt"
>>> a.split("\\")
['d:', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']

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

У rМодифікатор позначає строковий літерал , як «сирий»; зауважте, як вбудовані накипи не подвоюються.


@unwind - rперед вашим рядком, на що це йдеться?
BeeBand

2
r означає "необроблений рядок" - він автоматично уникає \ символів. Це корисно використовувати, коли ви робите доріжки.
Уейн Вернер

1
@BeeBand: вам не потрібно дбати; r "" - це лише те, що має значення під час компіляції / розбору коду, це не те, що стає властивістю рядка, щойно розбирається. Це просто означає, "ось рядок буквально, але не слід трактувати будь-які зворотні косої риси як такі, що мають інше значення, ніж те, що вони є косою рисою".
розмотувати

3
Я думаю, що може бути корисним згадати про вас мінус, а чи робити це більш неоднозначно, використовуючи a.split (os.sep) замість жорсткого кодування?
Тім МакДілтон

4
Мені потрібно подати заяву про те, що ви пропустили можливість пояснити, os.path.splitі os.pathsep, вважаючи, що це набагато переносніше, ніж те, що ви написали. Зараз це може не мати значення для ОП, але це буде, коли він пише щось, що потрібно для переміщення платформ.
Джед Сміт

3

Речі про приблизно mypath.split("\\")було б краще виразити як mypath.split(os.sep). sepє роздільником шляху для вашої конкретної платформи (наприклад, \для Windows, /Unix тощо), і збірка Python знає, яку саме використовувати. Якщо ви користуєтесь sep, то ваш код буде агностичним на платформі.


1
Або os.path.split. Ви хочете бути обережними os.pathsep, тому що це в :моїй версії Python в OS X (і os.path.splitправильно обробляє /).
Джед Сміт

4
Ти маєш на увазі os.sep, ні os.pathsep. Дотримуйтесь мудрості в os.sepдокументах: Зауважте, що знаючи цього недостатньо для того, щоб можна було розбирати чи об'єднувати імена шляхів - використовуйте os.path.split () та os.path.join ().
Джон-Ерік

1

re.split () може допомогти трохи більше ніж string.split ()

import re    
var = "d:\stuff\morestuff\furtherdown\THEFILE.txt"
re.split( r'[\\/]', var )
['d:', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']

Якщо ви також хочете підтримувати шляхи Linux та Mac, просто додайте фільтр (None, результат), щоб він видалив небажаний '' з split (), оскільки їх шляхи починаються з '/' або '//'. наприклад '// mount / ...' або '/ var / tmp /'

import re    
var = "/var/stuff/morestuff/furtherdown/THEFILE.txt"
result = re.split( r'[\\/]', var )
filter( None, result )
['var', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']

1

Ви можете рекурсивно os.path.splitвиконувати рядок

import os
def parts(path):
    p,f = os.path.split(path)
    return parts(p) + [f] if f else [p]

Тестуючи це на деяких рядках контуру та повторно збираючи шлях os.path.join

>>> for path in [
...         r'd:\stuff\morestuff\furtherdown\THEFILE.txt',
...         '/path/to/file.txt',
...         'relative/path/to/file.txt',
...         r'C:\path\to\file.txt',
...         r'\\host\share\path\to\file.txt',
...     ]:
...     print parts(path), os.path.join(*parts(path))
... 
['d:\\', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt'] d:\stuff\morestuff\furtherdown\THEFILE.txt
['/', 'path', 'to', 'file.txt'] /path\to\file.txt
['', 'relative', 'path', 'to', 'file.txt'] relative\path\to\file.txt
['C:\\', 'path', 'to', 'file.txt'] C:\path\to\file.txt
['\\\\', 'host', 'share', 'path', 'to', 'file.txt'] \\host\share\path\to\file.txt

Перший елемент списку, можливо, потребує трактування по-різному, залежно від того, як ви хочете мати справу з літерами диска, контурами UNC та абсолютними та відносними шляхами. Зміна останнього [p]до [os.path.splitdrive(p)]силам питання розщеплення букви диска і каталог викорчувати в кортеж.

import os
def parts(path):
    p,f = os.path.split(path)
    return parts(p) + [f] if f else [os.path.splitdrive(p)]

[('d:', '\\'), 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']
[('', '/'), 'path', 'to', 'file.txt']
[('', ''), 'relative', 'path', 'to', 'file.txt']
[('C:', '\\'), 'path', 'to', 'file.txt']
[('', '\\\\'), 'host', 'share', 'path', 'to', 'file.txt']

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


0

Так само, як пояснили інші - ваша проблема виникла з використання \, що є символом втечі в рядковому буквальному / постійному. OTOH, якби у вас був цей рядок шляху до файлу з іншого джерела (читання з файлу, консолі або повернуто функцією os) - не було б проблем з розщепленням на '\\' або r '\'.

І так, як пропонують інші, якщо ви хочете використовувати \в програмному літералі, ви повинні або дублювати його, \\або весь літерал повинен бути префіксом r, як-от так, r'lite\ral'або r"lite\ral"уникати аналізатора, який перетворює цей \і rв CR (повернення каретки) символ.

Є ще один спосіб - просто не використовуйте зворотні \коси імена в коді! З минулого століття Windows розпізнає і чудово працює з іменами шляхів, які використовують просунуту косу рису як роздільник каталогів /! Якось мало хто це знає .. але це працює:

>>> var = "d:/stuff/morestuff/furtherdown/THEFILE.txt"
>>> var.split('/')
['d:', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']

Це, до речі, змусить ваш код працювати в Unix, Windows та Mac ... тому що всі вони використовують /як роздільник каталогів ... навіть якщо ви не хочете використовувати заздалегідь задані константи модуля os.


На жаль, дані повертаються мені з іншої програми, яку я запускаю зі свого сценарію python. Я не маю ніякого контролю над тим, чи використовувати "\" чи "/" - це визначає стороння програма (можливо, на платформі).
BeeBand

@BeeBand: Ага, тоді у вас не виникне проблеми, яку ви відчули під час тестування, коли ви надали рядок як буквальний у своїй програмі. Або ви можете зробити наступний злий злом після отримання шляху: var = var.replace('\\','/')- замініть \ на / і продовжуйте роботу лише з пересічками вперед :)
Нас Банов

це справді злий злом: o)
BeeBand

@BeeBand: тому я попередив. Коли я кажу, що щось є зле, я не обов'язково маю на увазі, що його ніколи не слід використовувати, але слід дуже усвідомлювати, чому вони це використовують, і попереджувати про непередбачувані наслідки. У цьому випадку дуже малоймовірним наслідком є ​​те, що якщо це використовується у файловій системі Unix з `` використанням у файлі чи імені каталогу (це дійсно важко, але можливо) - цей код 'зламається'
Нас Банов

0

Припустимо, у вас є файл filedata.txtіз вмістом:

d:\stuff\morestuff\furtherdown\THEFILE.txt
d:\otherstuff\something\otherfile.txt

Ви можете читати та розділяти шляхи до файлів:

>>> for i in open("filedata.txt").readlines():
...     print i.strip().split("\\")
... 
['d:', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']
['d:', 'otherstuff', 'something', 'otherfile.txt']

це справді працює, дякую! Але я вибрав рішення Брона, тому що я вважаю за краще не турбуватися про те, щоб уникнути зворотної косої риси.
BeeBand

9
Не пітонічний, оскільки це залежить від файлової системи.
jb.

0

Я використовую наступне, оскільки він використовує функцію os.path.basename, вона не додає косих рисочок до поверненого списку. Він також працює з косою рисою будь-якої платформи: тобто \\ або unix's /. І крім того, він не додає \\\\, що Windows використовує для серверів шляхів :)

def SplitPath( split_path ):
    pathSplit_lst   = []
    while os.path.basename(split_path):
        pathSplit_lst.append( os.path.basename(split_path) )
        split_path = os.path.dirname(split_path)
    pathSplit_lst.reverse()
    return pathSplit_lst

Так для '\\\\ сервера \\ папка1 \\ папка2 \\ папка3 \\ папка4'

Ви отримуєте

['сервер', 'папка1', 'папка2', 'папка3', 'папка4']


1
Це не відповідає інваріанту, який, передаючи ваш результат, os.path.join()повинен повернути початковий рядок. Я б сказав, що правильний вихід для вашого прикладу є [r'\\','server','folder1','folder2','folder3','folder4']. Тобто що os.path.split()робить.
Джон-Ерік

0

Я насправді не впевнений, чи це повністю відповідає на питання, але я весело провів час написання цієї маленької функції, яка зберігає стек, дотримується маніпуляцій на основі os.path та повертає список / стек елементів.

  9 def components(path):
 10     ret = []
 11     while len(path) > 0:
 12         path, crust = split(path)
 13         ret.insert(0, crust)
 14
 15     return ret
 16

0

Нижче рядка коду може працювати:

  1. С: / шлях / шлях
  2. C: // шлях // шлях
  3. C: \ шлях \ шлях
  4. C: \ шлях \ шлях

path = re.split (r '[/// \]', шлях)


0

Один рекурсивний для розваги.

Не найвишуканіша відповідь, але повинна працювати всюди:

import os

def split_path(path):
    head = os.path.dirname(path)
    tail = os.path.basename(path)
    if head == os.path.dirname(head):
        return [tail]
    return split_path(head) + [tail]

дійсно, вибачте. Ви повинні уважно прочитати питання ... шлях 'dos'.
DuGNu

-1

використання ntpath.split()


коли я використовую os.path.split () я отримую, ( d:\\stuff, morestuff\x0curtherdown\thefile.mux)
BeeBand

Як вказував BeeBand, os.path.split () дійсно не робить потрібного.
розмотуємось

Вибачте, я щойно зрозумів, що os.path працює тільки в залежності від вашого ОС. ntpath буде розбирати dos шляху.
deft_code

навіть з ntpath я все-таки отримуюd:\\stuff, morestuff\x0curtherdown\thefile.mux
BeeBand

2
@BeeBand: у вас виникли проблеми з уникненням рядка. '\x0c'- це символ подачі форми. Спосіб створення символу подачі форми - "\ f". Якщо ви дійсно хочете буквального рядка '\ f', у вас є два варіанти: '\\f'або r'\f'.
deft_code
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.