кроссплатформенное разбиение пути в python - PullRequest
18 голосов
/ 02 января 2011

Я бы хотел что-то такое же, как это:

>>> path = "/foo/bar/baz/file"
>>> path_split = path.rsplit('/')[1:]
>>> path_split
['foo', 'bar', 'baz', 'file']

Но это будет работать и с путями Windows.Я знаю, что есть os.path.split(), но он не делает то, что я хочу, и я не видел ничего, что делает.

Ответы [ 8 ]

26 голосов
/ 07 июля 2015

Python 3.4 представил новый модуль pathlib. pathlib.Path предоставляет методы, связанные с файловой системой, а pathlib.PurePath работает полностью независимо от файловой системы:

>>> from pathlib import PurePath
>>> path = "/foo/bar/baz/file"
>>> path_split = PurePath(path).parts
>>> path_split
('\\', 'foo', 'bar', 'baz', 'file')

При желании вы можете использовать PosixPath и WindowsPath явно:

>>> from pathlib import PureWindowsPath, PurePosixPath
>>> PureWindowsPath(path).parts
('\\', 'foo', 'bar', 'baz', 'file')
>>> PurePosixPath(path).parts
('/', 'foo', 'bar', 'baz', 'file')

И, конечно, он также работает с путями Windows:

>>> wpath = r"C:\foo\bar\baz\file"
>>> PurePath(wpath).parts
('C:\\', 'foo', 'bar', 'baz', 'file')
>>> PureWindowsPath(wpath).parts
('C:\\', 'foo', 'bar', 'baz', 'file')
>>> PurePosixPath(wpath).parts
('C:\\foo\\bar\\baz\\file',)
>>>
>>> wpath = r"C:\foo/bar/baz/file"
>>> PurePath(wpath).parts
('C:\\', 'foo', 'bar', 'baz', 'file')
>>> PureWindowsPath(wpath).parts
('C:\\', 'foo', 'bar', 'baz', 'file')
>>> PurePosixPath(wpath).parts
('C:\\foo', 'bar', 'baz', 'file')

Huzzah для разработчиков Python, постоянно улучшающих язык!

19 голосов
/ 03 января 2011

Указанный OP "будет работать и с путями Windows".Есть несколько складок с путями Windows.

Во-первых, в Windows есть концепция нескольких дисков, каждый со своим текущим рабочим каталогом, и 'c:foo' и 'c:\\foo' часто не совпадают.Следовательно, это хорошая идея, чтобы сначала выделить любое обозначение диска, используя os.path.splitdrive ().Затем повторная сборка пути (если требуется) может быть выполнена правильно с помощью drive + os.path.join(*other_pieces)

Во-вторых, пути Windows могут содержать косую черту или обратную косую черту или смесь.Следовательно, использование os.sep при синтаксическом анализе ненормализованного пути бесполезно.

В целом:

Результаты, полученные для 'foo' и 'foo/', не должны быть идентичными.

Кажется, что условие завершения цикла лучше всего выражать как "os.path.split () обработал свой ввод как нерасщепляемый".

Вот предлагаемое решение с тестами, включая сравнение с решением @ Spacedman

import os.path

def os_path_split_asunder(path, debug=False):
    parts = []
    while True:
        newpath, tail = os.path.split(path)
        if debug: print repr(path), (newpath, tail)
        if newpath == path:
            assert not tail
            if path: parts.append(path)
            break
        parts.append(tail)
        path = newpath
    parts.reverse()
    return parts

def spacedman_parts(path):
    components = [] 
    while True:
        (path,tail) = os.path.split(path)
        if not tail:
            return components
        components.insert(0,tail)

if __name__ == "__main__":
    tests = [
        '',
        'foo',
        'foo/',
        'foo\\',
        '/foo',
        '\\foo',
        'foo/bar',
        '/',
        'c:',
        'c:/',
        'c:foo',
        'c:/foo',
        'c:/users/john/foo.txt',
        '/users/john/foo.txt',
        'foo/bar/baz/loop',
        'foo/bar/baz/',
        '//hostname/foo/bar.txt',
        ]
    for i, test in enumerate(tests):
        print "\nTest %d: %r" % (i, test)
        drive, path = os.path.splitdrive(test)
        print 'drive, path', repr(drive), repr(path)
        a = os_path_split_asunder(path)
        b = spacedman_parts(path)
        print "a ... %r" % a
        print "b ... %r" % b
        print a == b

и вот вывод (Python 2.7.1, Windows 7 Pro):

Test 0: ''
drive, path '' ''
a ... []
b ... []
True

Test 1: 'foo'
drive, path '' 'foo'
a ... ['foo']
b ... ['foo']
True

Test 2: 'foo/'
drive, path '' 'foo/'
a ... ['foo', '']
b ... []
False

Test 3: 'foo\\'
drive, path '' 'foo\\'
a ... ['foo', '']
b ... []
False

Test 4: '/foo'
drive, path '' '/foo'
a ... ['/', 'foo']
b ... ['foo']
False

Test 5: '\\foo'
drive, path '' '\\foo'
a ... ['\\', 'foo']
b ... ['foo']
False

Test 6: 'foo/bar'
drive, path '' 'foo/bar'
a ... ['foo', 'bar']
b ... ['foo', 'bar']
True

Test 7: '/'
drive, path '' '/'
a ... ['/']
b ... []
False

Test 8: 'c:'
drive, path 'c:' ''
a ... []
b ... []
True

Test 9: 'c:/'
drive, path 'c:' '/'
a ... ['/']
b ... []
False

Test 10: 'c:foo'
drive, path 'c:' 'foo'
a ... ['foo']
b ... ['foo']
True

Test 11: 'c:/foo'
drive, path 'c:' '/foo'
a ... ['/', 'foo']
b ... ['foo']
False

Test 12: 'c:/users/john/foo.txt'
drive, path 'c:' '/users/john/foo.txt'
a ... ['/', 'users', 'john', 'foo.txt']
b ... ['users', 'john', 'foo.txt']
False

Test 13: '/users/john/foo.txt'
drive, path '' '/users/john/foo.txt'
a ... ['/', 'users', 'john', 'foo.txt']
b ... ['users', 'john', 'foo.txt']
False

Test 14: 'foo/bar/baz/loop'
drive, path '' 'foo/bar/baz/loop'
a ... ['foo', 'bar', 'baz', 'loop']
b ... ['foo', 'bar', 'baz', 'loop']
True

Test 15: 'foo/bar/baz/'
drive, path '' 'foo/bar/baz/'
a ... ['foo', 'bar', 'baz', '']
b ... []
False

Test 16: '//hostname/foo/bar.txt'
drive, path '' '//hostname/foo/bar.txt'
a ... ['//', 'hostname', 'foo', 'bar.txt']
b ... ['hostname', 'foo', 'bar.txt']
False
18 голосов
/ 02 января 2011

Кто-то сказал «используйте os.path.split». К сожалению, это было удалено, но это правильный ответ.

os.path.split (путь)

Разделите путь к пути в пару (голова, хвост), где хвост - это последний компонент имени пути, а голова - это все, что ведет к этому. Хвостовая часть никогда не будет содержать косую черту; если путь заканчивается косой чертой, хвост будет пустым. Если в пути нет косой черты, голова будет пустой. Если путь пуст, голова и хвост пусты. Конечные косые черты снимаются с головы, если это не корень (только одна или несколько косых черт). Во всех случаях join (head, tail) возвращает путь в то же место, что и path (но строки могут отличаться).

Так что это не просто разделение имени и имени файла. Вы можете применить его несколько раз, чтобы получить полный путь в переносном и правильном виде. Пример кода:

dirname = path
path_split = []
while True:
    dirname, leaf = split(dirname)
    if leaf:
        path_split = [leaf] + path_split #Adds one element, at the beginning of the list
    else:
        #Uncomment the following line to have also the drive, in the format "Z:\"
        #path_split = [dirname] + path_split 
        break

Пожалуйста, укажите оригинального автора, если этот ответ будет восстановлен.

4 голосов
/ 02 января 2011

Используйте функциональность, предоставленную в os.path, например,

os.path.split(path)

Как написано в другом месте, вы можете вызывать его несколько раз, чтобы разделить более длинные пути.

3 голосов
/ 21 ноября 2014

Вот явная реализация подхода, который просто итеративно использует os.path.split;использует немного другое условие завершения цикла, чем принятый ответ.

def splitpath(path):
    parts=[]
    (path, tail)=os.path.split( path)
    while path and tail:
         parts.append( tail)
         (path,tail)=os.path.split(path)
    parts.append( os.path.join(path,tail) )
    return map( os.path.normpath, parts)[::-1]

Это должно удовлетворить os.path.join( *splitpath(path) ) is path в том смысле, что они оба указывают на один и тот же файл / каталог.

Протестировано в Linux:

In [51]: current='/home/dave/src/python'

In [52]: splitpath(current)
Out[52]: ['/', 'home', 'dave', 'src', 'python'] 

In [53]: splitpath(current[1:])
Out[53]: ['.', 'dave', 'src', 'python']

In [54]: splitpath( os.path.join(current, 'module.py'))
Out[54]: ['/', 'home', 'dave', 'src', 'python', 'module.py']

In [55]: splitpath( os.path.join(current[1:], 'module.py'))
Out[55]: ['.', 'dave', 'src', 'python', 'module.py']

Я вручную проверил несколько путей DOS, используя замену os.path на модуль ntpath, выглядело нормально для меня, но я 'Я не слишком знаком с входами и выходами путей DOS.

3 голосов
/ 02 января 2011

Используйте функциональность, представленную в os.path, например,

os.path.split(path)

(Этот ответ был кем-то другим и был загадочно и неправильно удален, так как это рабочий ответ; если вы хотите разделить каждую часть пути на части, вы можете вызывать его несколько раз, и каждый вызов отключит компонент конца.)

0 голосов
/ 07 ноября 2012

Еще одна попытка с опцией maxplit, которая заменяет os.path.split ()

def pathsplit(pathstr, maxsplit=1):
    """split relative path into list"""
    path = [pathstr]
    while True:
        oldpath = path[:]
        path[:1] = list(os.path.split(path[0]))
        if path[0] == '':
            path = path[1:]
        elif path[1] == '':
            path = path[:1] + path[2:]
        if path == oldpath:
            return path
        if maxsplit is not None and len(path) > maxsplit:
            return path
0 голосов
/ 02 января 2011

Так что продолжайте использовать os.path.split, пока не получите то, что хотите.Вот уродливая реализация, использующая бесконечный цикл:

import os.path
def parts(path):
    components = [] 
    while True:
        (path,tail) = os.path.split(path)
        if tail == "":
            components.reverse()
            return components
        components.append(tail)

Вставьте это в parts.py, импортируйте части и вуаля:

>>> parts.parts("foo/bar/baz/loop")
['foo', 'bar', 'baz', 'loop']

Вероятно, более хорошая реализация, использующая генераторы или рекурсию там...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...