Как разделить путь DOS на его компоненты в Python

154

У меня есть строковая переменная, которая представляет путь DOS, например:

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

Я хочу разбить эту строку на:

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

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

Мне нужно как-то преобразовать эту строковую переменную в необработанную строку, чтобы я мог ее проанализировать.

Какой лучший способ сделать это?

Я также должен добавить, что содержимое varпути, который я пытаюсь проанализировать, на самом деле является возвращаемым значением запроса командной строки. Это не данные пути, которые я генерирую сам. Он хранится в файле, и инструмент командной строки не избежит обратной косой черты.

BeeBand
источник
6
Просматривая эти ответы, помните, что os.path.splitэто не работает для вас, потому что вы не экранируете эту строку должным образом.
Джед Смит
Вам нужно экранировать строку или использовать rawstring: r"d:\stuff\morestuff\furtherdown\THEFILE.txt"для предотвращения \sнеправильного толкования.
SMCI

Ответы:

165

Меня много раз кусали люди, которые писали свои собственные функции путаницы и неправильно понимали. Пробелы, косая черта, обратная косая черта, двоеточие - возможности для путаницы не бесконечны, но ошибки в любом случае легко допускаются. Так что я сторонник использования 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если путь изначально был абсолютным. Вы можете потерять немного кода, если не хотите этого.)

HunnyBear
источник
@ 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должно быть безопасно использовать в качестве разделителя в строковой функции split.

Tompa
источник
25
Единственный верный ответ: он появился . Каноническое решение, конечно, самое простое. Вот! Потому что это элегантно и многозначительно и не имеет невыносимых крайних случаев.
Сесил Карри
20
Как однострочник,os.path.normpath(a_path).split(os.path.sep)
Даниэль Фаррелл
2
Это не похоже на путь = root. В этом случае результатом path.split будет ['', '']. Фактически, в общем, это решение split () дает крайний левый каталог с именем пустой строки (который может быть заменен соответствующей косой чертой). Основная проблема заключается в том, что одиночная косая черта (вперед или назад в зависимости от ОС) - это имя корневого каталога, тогда как в других местах пути это разделитель .
человек
2
Будет ли это лучше работать с полоской? os.path.normpath(path).lstrip(os.path.sep).split(os.path.sep)
Видар
1
@ user60561 Это потому, что в Linux обратная косая черта является разрешенным символом в именах файлов, а в Windows косая черта - нет. Вот почему в Windows normpathраспознает косую черту как разделитель. В Linux normpathпросто предполагается, что у вас есть каталог с именем \1\2и файл или каталог внутри него 3.
Воислав Стойкович
81

Вы можете просто использовать самый Pythonic подход (IMHO):

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]
Maciek D.
источник
22
Это работает some times. В других случаях (по крайней мере, в Windows) вы найдете пути, которые выглядят следующим образом folder\folder2\folder3/file.txt. Лучше сначала нормализовать (os.path.normpath) путь, а затем разделить его.
Викки
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')

При установке Python 3 для Windows это предполагает, что вы работаете с путями 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

freidrichen
источник
3
Path.parts - это то, что я всегда хотел, но я никогда не знал, что он существует до сегодняшнего дня.
JamEnergy
почему это не обернуто вокруг хорошей функции Python?
Эдуардо
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. Крейг Трейдер - спасибо, но этот путь не тот, который я генерирую сам - он возвращается ко мне из другой программы, и я должен сохранить эти данные в переменной. Я не уверен, как преобразовать данные, хранящиеся в переменной в «сырой текст».
BeeBand
Нет такого понятия, как «необработанный текст», просто вы представляете его в источнике. Либо добавьте r "" к строке, либо пропустите ее через .replace ('\\', '/')
Marco Mariani
@BeeBand, как ты возвращаешь данные из другой программы? Вы читаете это из файла, трубы, сокета? Если это так, то вам не нужно делать что-то необычное; единственная причина для удвоения обратной косой черты или использования необработанных строк - это помещение строковых констант в код Python. С другой стороны, если другая программа генерирует двойные обратные слеши, то вы захотите очистить ее, прежде чем разбивать путь.
Крейг Трейдер
@W. Крейг Трейдер - я читаю его из файла, который пишется другой программой. Я не мог получить 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]
user1556435
источник
Это мое любимое решение этой проблемы. Очень хорошо.
Уилл Мур
1
Это не работает, если путь заканчивается /. Кроме того, выдает пустую строку в начале списка, если ваш путь начинается с/
Сориг
4

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

antred
источник
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']
Benoit
источник
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, учитывая, что оба они гораздо более портативны, чем то, что вы написали. Возможно, сейчас OP не имеет значения, но будет, когда он пишет что-то, что нужно для перемещения платформ.
Джед Смит
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, result), чтобы он удалил нежелательные '' из 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

Как и другие объяснили, ваша проблема возникла из-за использования \escape-символа в строковом литерале / константе. 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('\\','/')- замените \ на / и продолжайте работать только с косыми чертами :)
Нас Банов
это действительно злой хак: о)
BeeBand
@BeeBand: вот почему я предупредил. Когда я говорю, что что-то является злом, я не обязательно имею в виду, что это никогда не должно использоваться, но нужно очень хорошо понимать, почему они это используют, и предупреждать о непредвиденных последствиях. В этом случае очень маловероятным последствием является то, что если это используется в файловой системе Unix с `` использованием в имени файла или каталога (это действительно сложно, но возможно) - этот код "сломается"
Nas Banov
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']
zoli2k
источник
это действительно работает, спасибо! Но я выбрал решение Brone, потому что я предпочитаю не беспокоиться о том, чтобы избежать обратной косой черты.
BeeBand
9
Не pythonic, так как это зависит от файловой системы.
JB.
0

Я использую следующее, поскольку, поскольку он использует функцию os.path.basename, он не добавляет косые черты в возвращаемый список. Он также работает с косыми чертами любой платформы: то есть с окном \\ или unix /. И, кроме того, он не добавляет \\\\, который 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

Так что для '\\\\ server \\ folder1 \\ folder2 \\ folder3 \\ folder4'

ты получаешь

[ 'Сервер', 'Folder1', 'folder2', 'folder3', 'Folder4']

сойка
источник
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
mallyvai
источник
0

Ниже строка кода может обрабатывать:

  1. C: / путь / путь
  2. C: // путь // путь
  3. C: \ путь \ путь
  4. C: \ путь \ путь

путь = 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]
DuGNu
источник
действительно, извините. Надо было внимательно прочитать вопрос ... путь "до".
DuGNu
-1

использование ntpath.split()

deft_code
источник
когда я использую os.path.split (), я получаю, ( d:\\stuff, morestuff\x0curtherdown\thefile.mux)
BeeBand
Как указал BeeBand, os.path.split () действительно не выполняет желаемых действий.
расслабиться
извините, я только что понял, что os.path работает только в зависимости от вашей ОС. ntpath проанализирует все пути
deft_code
даже с ntpath я все равно получаюd:\\stuff, morestuff\x0curtherdown\thefile.mux
BeeBand
2
@BeeBand: у вас проблемы с выходом из строки. '\x0c'является символом подачи формы. Способ создания символа фида формы - «\ f». Если вы действительно хотите буквальную строку '\ f', у вас есть два варианта: '\\f'или r'\f'.
deft_code