Кросс-платформенное расщепление пути в python
Мне нужно что-то, что имеет такой же эффект, как и этот:
>>> path = "/foo/bar/baz/file"
>>> path_split = path.rsplit('/')[1:]
>>> path_split
['foo', 'bar', 'baz', 'file']
Но это тоже будет работать с путями Windows. Я знаю, что есть os.path.split()
, но это не делает то, что я хочу, и я не видел ничего, что делает.
Ответы
Ответ 1
Указанный 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
Ответ 2
Кто-то сказал "use os.path.split
". К сожалению, это удалено, но это правильный ответ.
os.path.split(путь)
Разделите путь пути в пару (head, tail), где tail - это последний компонент pathname, а head - все, что до этого. Хвостовая часть никогда не будет содержать косой черты; если путь заканчивается косой чертой, хвост будет пустым. Если в пути нет косой черты, голова будет пустой. Если путь пуст, обе головы и хвоста пусты. Косые косые черты удаляются из головы, если они не являются корнями (только одна или несколько косых черт). Во всех случаях join (head, tail) возвращает путь к тому же местоположению, что и путь (но строки могут отличаться).
Таким образом, это не просто разделение имени и имени файла. Вы можете применить его несколько раз, чтобы получить полный путь портативным и правильным способом. Пример кода:
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
Пожалуйста, воздайте оригинал оригиналу, если этот ответ будет восстановлен.
Ответ 3
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 постоянно улучшает язык!
Ответ 4
Используйте функции, указанные в os.path
, например
os.path.split(path)
Как и в других местах, вы можете вызвать его несколько раз, чтобы разделить более длинные пути.
Ответ 5
Используйте функции, предоставляемые в os.path, например.
os.path.split(path)
(Этот ответ был кем-то другим и был загадочно и неправильно удален, так как это рабочий ответ, если вы хотите разделить каждую часть пути друг от друга, вы можете вызывать его несколько раз, и каждый вызов вытаскивает компонент конца.)
Ответ 6
Здесь явная реализация подхода, который только итеративно
использует 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-путей.
Ответ 7
Еще одна попытка с опцией 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
Ответ 8
Так что продолжайте использовать 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']
Возможно, более приятная реализация с использованием генераторов или рекурсии там...